You type example.com, hit Enter, and a page appears. In the fraction of a second between
those two events, your browser opened a
This page is about what that text actually says. We will take a request apart line by line, do the same for a response, meet the handful of methods and status codes you need, and confront the single idea that shapes everything built on top of HTTP — it is stateless: the server remembers nothing about you between one request and the next.
HTTP is a strict request/response protocol. The client always speaks first with a request; the server answers with exactly one response; then that exchange is over. The server never spontaneously sends you anything — it only ever replies. Here is a real request, in full, exactly as it travels down the socket (the blank line is load-bearing — hold that thought):
And the response the server sends back:
Both messages share the same four-part shape: a start line, some
headers (one Name: value pair per line), a single
blank line, and an optional body. The blank line is how the parser
knows the headers have ended and the body (if any) begins — it is the frame boundary of the whole
format. Everything else is just filling in those four slots.
The request's start line has three whitespace-separated fields — method, path, version — and then the headers do the rest of the talking.
| Part | Example | What it says |
|---|---|---|
| Method | GET | the verb — what to do with the resource |
| Path (target) | /articles/42 | which resource on this server |
| Version | HTTP/1.1 | which dialect of the protocol |
Host | example.com | which website (one IP can host thousands) |
User-Agent | Mozilla/5.0 | what software is asking |
Accept | text/html | which representations the client can use |
| Body | (often empty) | data being sent up, e.g. a submitted form |
The Host header deserves a spotlight: it is the reason a single server at a single IP
address can serve example.com, example.org and ten thousand other sites. The
path /articles/42 alone is ambiguous — Host disambiguates which site's
/articles/42 you mean. It is mandatory in HTTP/1.1 for exactly this reason.
The response's start line is the status line: version, a three-digit
status code, and a human-readable reason phrase (200 OK). The
code is what your software reads; the phrase is a courtesy for humans. The first digit sorts every
code into one of five classes:
| Class | Meaning | Familiar members |
|---|---|---|
| 1xx | Informational — "still going" | 100 Continue, 101 Switching Protocols |
| 2xx | Success — "here you go" | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection — "look elsewhere" | 301 Moved Permanently, 304 Not Modified |
| 4xx | Client error — "you messed up" | 400 Bad Request, 401 Unauthorized, 404 Not Found |
| 5xx | Server error — "I messed up" | 500 Internal Server Error, 503 Service Unavailable |
The 4xx/5xx split is a genuinely useful mental model when something breaks: a 4xx says the request itself was wrong (bad URL, missing auth, malformed data) — retrying it unchanged won't help. A 5xx says the request was fine but the server failed — retrying might help once the server recovers. Whole retry and monitoring strategies are built on that one distinction.
The three-digit scheme is inherited from much older text protocols like FTP and SMTP, where a
machine needed to branch on a reply without parsing English. The tens and units digit give
fine detail, but a dumb client can ignore them and just switch on the first digit: 2 = good, 4 = my
fault, 5 = their fault. That is why you can write robust code that handles a status code it has never
seen before — 418 I'm a teapot and all — simply by bucketing it as
Math.floor(code / 100). The design has aged for fifty years without a rewrite.
The method is the verb. Five carry the everyday load, and the specification pins each with two properties that matter enormously for correctness on an unreliable network.
GET and HEAD are safe; a crawler can fire them freely.
PUT, DELETE, GET
and HEAD are idempotent; POST is not.
DELETE changes state (not safe)
yet deleting twice is the same as deleting once (idempotent).
This is not pedantry — it is what lets the network retry. If your connection drops mid-request,
a client can safely re-send a GET or PUT without fear of double-acting. It
cannot blindly retry a POST that says "charge my card £50", because a second
delivery might charge you twice. Idempotency is the property that makes an unreliable network usable.
Because HTTP is just text with a rigid shape, parsing it is pure string logic — no network needed. The demo below takes a raw request exactly as it would arrive on a socket, splits it at the blank line into headers and body, pulls apart the request line, and builds a structured object. This is, in miniature, what every web server does on every request.
Notice how Content-Length earns its keep: since the underlying stream has
Transfer-Encoding: chunked) the server could not tell "the body
is done" from "the network is just slow".
Here is the defining property of HTTP: it is stateless. Each request/response pair is an island. The server processes your request, sends the reply, and forgets you completely. Your next request arrives as if from a total stranger — the server holds no memory of the last one. Everything the server needs to handle a request must travel inside that request.
This sounds like a limitation, and it is the reason
Statelessness is often confused with connectionlessness, but they are different layers. Early
HTTP/1.0 did open a fresh TCP connection for every single request and slam it shut after the response —
one file, one connection, one handshake. On a page with 50 images that meant 50 handshakes, a wretched
waste. HTTP/1.1 made persistent connections (aka keep-alive) the default: the
TCP connection stays open and many request/response pairs flow over it, one after another. The protocol
is still stateless — the server remembers nothing between requests — but the expensive
The commonest beginner mistake is to imagine the server "knows who you are" the way a shop assistant remembers a customer who is standing in front of them. It does not. When you are logged into a site and click three links, those are three unrelated requests; the server has no built-in thread tying them to one user. If it appears to remember you, that is only because your browser attached a cookie (or a token) to every request, carrying an identifier the server can look up. Pull that cookie out and the server treats each click as a fresh anonymous stranger. Nothing about "being logged in" lives in the connection or the protocol — the state is smuggled along inside every single request, precisely because HTTP itself keeps none.
"Safe" in HTTP has a narrow technical meaning — no server-side side effects — and it says
nothing about security or privacy. Two real traps follow. First, a GET is only safe if
your handler keeps it read-only; a route like GET /account/delete?id=5 that
actually deletes something violates the contract, and web crawlers (which fire GETs freely) will
merrily wipe your data. Put state-changing actions behind POST/PUT/DELETE.
Second, GET parameters ride in the URL, so they land in browser history, server logs and
Referer headers — never put a password or token in a query string. "Safe" means
side-effect-free, not confidential.