HTTP: Explanation & Insights
The request-and-response protocol web browsers and servers use to exchange web pages and data; the foundation of the World Wide Web.
What It Is
HTTP — the HyperText Transfer Protocol — is the language your browser and a web server speak to move pages, images, and data across the internet. At heart it is almost embarrassingly simple: the client sends a request ("please give me this thing"), the server sends back a response ("here it is" — or "no"). That single round-trip, repeated billions of times a second, is the entire web.
Two things make it what it is. First, it's a request→response protocol: the client always speaks first and the server only ever answers — it never rings you back out of the blue. Second, it's text-based and human-readable (at least through HTTP/1.1): a request is a few lines of plain ASCII you could type by hand, which is exactly why it spread so fast, and why you can still debug it with nothing but curl and your own two eyes.
HTTP rides on top of a reliable connection; it doesn't move the bytes itself. That job belongs to TCP, the layer below, which guarantees the bytes arrive in order and intact. HTTP only decides what those bytes mean. Wrap the whole conversation in encryption and you get HTTPS — the same exchange sealed inside TLS so nobody on the wire can read over your shoulder. And before a word is spoken, your browser has to turn a name like example.com into an address it can actually reach: that's DNS's job, not HTTP's. This page is about the conversation itself — its shape, its verbs, its famous reply codes, and the slow forty-year climb from one line of text to the protocol behind nearly everything you do online.
The Shape of a Request
Here's what surprises people the first time they see it: an HTTP request is just text. Through HTTP/1.1 there was no binary handshake, no proprietary blob — you could open a raw connection and type the request out by hand. It looks like this:
GET /index.html HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Three parts, and that's the lot. The first line is the request line: a method (GET), a path (/index.html), and the protocol version. Then a stack of headers — Key: Value pairs carrying everything the server might want to know about who's asking and what they'll accept. Then a blank line, and optionally a body (data you're sending up, like a filled-in form).
The response wears exactly the same shape, just with a verdict on the first line instead of a request:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1256
Server: nginx
<!doctype html><html>...
A status line, headers, a blank line, a body. Request and response are mirror images — the same envelope, addressed in opposite directions. See it once and you can read any HTTP exchange in the world.
The Methods (The Verbs)
The method is the verb of the sentence: it tells the server what you want done to the thing at that path. A handful matter.
| Method | What it means | Changes anything? |
|---|---|---|
GET |
Fetch this resource. The workhorse — every page load is a GET. |
No (read-only) |
POST |
Send this data up; create or process something. | Yes |
PUT |
Put this resource here, replacing whatever's there. | Yes |
DELETE |
Remove this resource. | Yes |
HEAD |
Like GET, but send me only the headers, not the body. |
No |
PATCH |
Modify part of this resource. | Yes |
OPTIONS |
Tell me what methods you'll even allow here. | No |
The distinction worth carrying around is safe versus not. A GET is meant to be safe: it reads, it changes nothing, and you can repeat it as often as you like. That's not mere etiquette — it's a contract the whole web leans on. It's why a browser will happily pre-fetch and cache GET responses, and why building a "delete" button as a plain GET link is a classic rookie disaster: the first search-engine crawler to wander by clicks every link on the page, and now your database is empty. A crawler will never fire a POST or DELETE on its own. Put your destructive actions behind the verbs that mean destruction, and the web's own conventions stand guard for you.
Warning
Never wire a destructive action to a
GET. Crawlers, browser pre-fetchers, and "link preview" bots all followGETlinks automatically and silently — aGET /delete?id=42can be triggered by something that never meant to, and never even loaded your page.
The Status Codes
The server's answer always opens with a three-digit number, and the first digit tells you the whole story before you read a word more. Five families:
- 1xx — Informational. "Hold on, still working." Rare in the wild; mostly protocol plumbing like
100 Continue. - 2xx — Success. It worked.
200 OKis the one you want, all day, every day. - 3xx — Redirection. "It's not here, it's over there."
301 Moved Permanently(update your bookmark),302 Found(temporary detour),304 Not Modified(you already have the latest copy — use your cache, save us both the bandwidth). - 4xx — You messed up. The client sent something wrong.
404 Not Found(no such page),400 Bad Request(malformed),403 Forbidden(I know who you are and you still can't),429 Too Many Requests(slow down). - 5xx — I messed up. The server broke on a request that was perfectly fine. This is the family that ruins your evening.
The 4xx-versus-5xx line is the single most useful thing in the whole list, and people get it backwards constantly. 4xx is the client's fault; 5xx is the server's. If your browser shows a 404, the page genuinely isn't there — that's not the server malfunctioning, that's the server working perfectly and telling you the truth. A 500, by contrast, means the request was fine and the server fell over trying to honour it. One is "you asked for the wrong thing"; the other is "the thing you asked for is on fire."
And a pair people muddle for years: 401 Unauthorized actually means unauthenticated — "I don't know who you are, log in." 403 Forbidden means "I know exactly who you are, and you still don't get in." One's a locked door; the other's a bouncer who's found your name on the list and shaken his head.
The 5xx Family an Admin Actually Debugs
Out in the real world, most of the 5xx errors you'll chase never come from your application at all — they come from the reverse proxy or load balancer sitting in front of it, like nginx or HAProxy. The proxy takes your request, tries to hand it to the real app behind it (the "upstream"), and reports back on how that went. Three codes tell three completely different stories, and knowing which is which buys you back an hour every time:
| Code | What the proxy is telling you | Where to look |
|---|---|---|
502 Bad Gateway |
"I reached the app, but it answered with garbage." | The app crashed, or isn't speaking HTTP — check if the process is even running |
503 Service Unavailable |
"I can't serve this right now." | Overloaded, or deliberately down for maintenance — often the app has no healthy workers left |
504 Gateway Timeout |
"I asked the app and waited, and waited, and it never answered." | The app is alive but stuck — a slow query, a deadlock, an upstream of its own hanging |
The gap between a 502 and a 504 is the gap between a colleague who hangs up on you and one who simply never picks up. The first is a fast, clean failure — the app is down. The second is the worse one to debug, because the app is up; it's just frozen mid-thought, usually waiting on a database that's waiting on a disk that's waiting on nothing at all.
Two Codes That Are Real, And Are Jokes
Among the dry catalogue of 404s and 500s, two genuine, registered status codes are worth knowing — not because you'll ever serve them, but because they're the rare moment the people who build the internet's plumbing let their guard down.
418 I'm a teapot. On April Fools' Day 1998 the IETF — the very serious body that standardises internet protocols — published RFC 2324, the Hyper Text Coffee Pot Control Protocol: a deadpan satire of how people kept bolting features onto HTTP that had no business being there. Any attempt to brew coffee with a teapot, it rules, must return 418 I'm a teapot, and the spec earnestly adds that the response body "MAY be short and stout" — a straight-faced nod to the nursery rhyme, written into a formal standards document. It should have died as a one-day gag. Instead it escaped into real software. In 2017 a senior standards official moved to purge 418 from the major web frameworks for cluttering the registry, and a fifteen-year-old named Shane Brunswick stood up a site called save418.com arguing that the code "is a reminder that the underlying processes of computers are still made by humans." The internet sided with the teenager. Node.js, Go, Python, and ASP.NET all kept the teapot. The committee blinked.
451 Unavailable For Legal Reasons. This one began as a joke too, and grew up. When a page is blocked by a court order or a government — censored, not merely missing — the old habit was to return 403 Forbidden, which told you nothing about why. So 451 was proposed and, in 2015, formally standardised to mean exactly "this is gone because the law made it gone." The number is no accident: it's a straight salute to Ray Bradbury's Fahrenheit 451, the 1953 novel about a future where books are outlawed and burned — 451°F being the temperature at which paper catches fire. A status code about censorship, numbered after the most famous novel ever written about censorship. The people who pick these numbers read books, and every so often they let you catch them at it.
Headers: The Fine Print
If the method and path are the sentence, the headers are the whole negotiation around it — how the client and server settle the details (what format the body's in, what languages the client accepts, whether a cached copy is still fresh) without anyone having to ask out loud. There are a great many of them.
The one header that's quietly load-bearing is Host. Back in HTTP/1.0 it didn't exist, and didn't need to — one server, one website, one IP address. But the moment people wanted to run many sites on one server (and they wanted that almost immediately), the server hit a problem: a request lands at its address, but which of the forty websites it hosts does this person actually want? The Host header is the answer — the client names the site it's after — and making it mandatory in HTTP/1.1 is the single change that made cheap shared web hosting possible. Nearly every site you've ever visited shares its server's address with strangers, and Host is the line that keeps them from opening each other's mail.
The flip side of all that flexibility is sprawl. The list of registered HTTP headers runs to hundreds you'll never touch, and the genuinely strange ones (X-Clacks-Overhead, a tribute header some sites still send in memory of an author) hint at how much an open, text-based, "anyone can add a header" protocol accumulates over thirty years. HTTP grew like a coastline, not a building.
Statelessness, and Why Cookies Exist
Here's the property that defines HTTP and trips up everyone who learns it: HTTP is stateless. Every request is an island. The server answers it, and then — by design — forgets you completely. Your next request arrives as if from a total stranger. No thread of conversation, no "as I was saying," no memory of the page you loaded one second ago. And the forgetting is the whole point: statelessness is precisely what lets one server field millions of requests from millions of people without keeping a little notebook open for each of them.
Which raises an obvious problem. If the server forgets you the instant it answers, how does a shopping cart work? How does staying logged in work? How does anything that spans more than one click work?
The answer is one of my favourite true stories in computing, because the reason is so wonderfully human. In June 1994 a programmer named Lou Montulli at Netscape was building an online store for the telecom company MCI — and MCI flatly refused to let their servers hold half-finished orders; they didn't want a notebook of partial transactions for every shopper cluttering up their machines. So Montulli turned the problem inside out: if the server won't remember you, make the browser carry the memory instead. The server hands back a small token in a Set-Cookie header, and the browser dutifully returns that token in a Cookie header on every later request, reminding the forgetful server who it's talking to. The shopping cart lives on your machine, and you hand it back across the counter each time.
He called the token a cookie — borrowed from "magic cookie," an old Unix term for a small chunk of data a program receives and hands back unchanged, which itself traces back to the fortune cookie. So the thing that tracks you across the entire web — the subject of every privacy law and consent banner you've ever angrily clicked through — is named, three steps removed, after the slip of paper in a dessert. The whole scaffolding of logged-in, personalised, remembered web experience exists because one phone company in 1994 couldn't be bothered with the bookkeeping, and the fix was to make your own browser keep the receipts.
How HTTP Grew Up
HTTP has been quietly rewritten under our feet four times, each version solving the bottleneck the last one created. The shape of the conversation barely budged; the way the bytes travel changed enormously.
HTTP/0.9 (1991). Tim Berners-Lee's original, and it is almost unbelievably small. A request was one line — GET /page.html — and that was the entire protocol. One method, no headers, no status codes: the server sent back the document and hung up. They call it "the one-line protocol," and it ran the whole World Wide Web at its birth.
HTTP/1.0 (1996). Headers arrived, status codes arrived, and HTTP could finally carry more than raw HTML — images, forms, the lot. But it had an expensive habit: it opened a fresh TCP connection for every single file, then slammed it shut. A page with twenty images meant twenty separate connections, each paying the full setup cost from scratch — like hanging up and redialling between every sentence of a phone call.
HTTP/1.1 (1997). The version that ran the web for nearly two decades, and still does in plenty of places. Its big gift was keep-alive: reuse one connection for many requests instead of redialling each time. It also made Host mandatory (cheap shared hosting, see above). But it carried a stubborn flaw called head-of-line blocking — requests on a connection had to come back roughly in order, so one slow response could stall everything queued behind it, like a single dawdler holding up the whole supermarket checkout.
HTTP/2 (2015). Google had been experimenting with a faster protocol called SPDY, and HTTP/2 grew out of it. It traded human-readable text for compact binary framing and introduced multiplexing: many requests and responses interleaved over a single connection, all in flight at once, nobody waiting their turn. It solved head-of-line blocking — at the HTTP layer. But TCP underneath still insists every byte arrive in strict order, so one lost packet would freeze all the multiplexed streams until it was resent. The traffic jam had simply moved one floor down.
HTTP/3 (2022). The radical fix: stop using TCP at all. HTTP/3 runs on a new transport called QUIC, built on UDP, where each stream is independent — a lost packet stalls only its own stream and the others sail right past it. It folds the encryption handshake into the connection setup, so a fresh HTTPS connection comes up faster than it has any right to. Three decades after one line of text, the web's protocol walked off the very transport layer it was born on — and almost nobody felt the floor move.
Reading It by Example
A few exchanges, and what they tell you at a glance:
HTTP/2 200— the happy path. It worked; the page is served.HTTP/1.1 301 Moved Permanentlywith aLocation:header — the resource lives at a new URL now; your browser will follow it, and you should update any link you control.HTTP/1.1 304 Not Modifiedwith an empty body — you asked, the server checked your cached copy's fingerprint, and it's still current. No data sent. This is caching doing its quiet, invisible job.HTTP/1.1 404 Not Found— the page genuinely isn't there. The server is fine; the URL is wrong.HTTP/1.1 502 Bad Gatewayfrom your own site — your reverse proxy is up but the app behind it is down or crashed. Check whether the application process is even running.HTTP/1.1 504 Gateway Timeout— the app is alive but stuck. Look for a slow database query or a hung upstream, not a dead process.
Your First Look
The fastest way to see HTTP is to ask for nothing but the headers. The -I flag on curl sends a HEAD request — "show me the status and headers, skip the body":
curl -sI https://example.com
HTTP/2 200
date: Wed, 24 Jun 2026 10:51:47 GMT
content-type: text/html
server: cloudflare
last-modified: Fri, 19 Jun 2026 18:46:03 GMT
cf-cache-status: HIT
The first line is the verdict — protocol version and status code. Everything beneath it is the server's fine print: what it's serving (content-type), when it last changed (last-modified), which software is answering (server), and — here — that a CDN had a cached copy ready to hand straight back (cf-cache-status: HIT). Two minutes with curl -sI against a site that's misbehaving usually tells you more than an hour of guessing.
Pro Tip
When a site "isn't working," the status code is the first question, not the last.
curl -sI <url>in one second separates a404(your URL is wrong), a403(you're blocked), a502(the app crashed), and a504(the app is hung) — four completely different problems that all look identical in a browser as "the page didn't load."
See Also
- TCP — the reliable byte-stream HTTP rides on; where head-of-line blocking actually lives
- HTTPS — HTTP sealed inside TLS, which is nearly all web traffic today
- DNS — turns the hostname into an address before HTTP can say a word
- UDP — the connectionless transport under QUIC and HTTP/3
- reverse proxy — the thing in front of your app that issues most of the
5xxcodes you'll debug curl— the simplest way to send a raw HTTP request and read the reply- nginx — the web server and reverse proxy you'll meet first
- 502 Bad Gateway — when the proxy is up but the app isn't
Is your site throwing 502s and 504s while you're asleep?
CleverUptime watches your server's health from the outside and from within, so when the app behind your proxy crashes or hangs, you hear it from us — not from an angry customer.
Want to see your own server's health right now? One command, no signup, no install.