HTTP is stateless and request/response: the client always speaks first. That simple rule has shaped, and limited, everything built on top of it — including why WebSocket and gRPC exist at all.
| Version | Year | What changed, and why |
|---|---|---|
| 0.9 | 1991 | One line, one method (GET), no headers, HTML only. The connection closed after every response. |
| 1.0 | 1996 | Added headers, status codes, other content types — but still opened a brand new TCP connection (and did a fresh handshake) for every single request. |
| 1.1 | 1997 | Added persistent connections (keep-alive) so multiple requests could reuse one TCP connection — but browsers still had to open ~6 parallel connections per host to load a page's assets concurrently, each paying its own setup cost. |
| 2 | 2015 | Binary framing lets many logical streams share one TCP connection (multiplexing), plus header compression. Fixes the "too many connections" problem — but a lost TCP segment still stalls every stream, because they all share one ordered TCP byte stream underneath. |
| 3 | 2022 | Runs over QUIC instead of TCP. Each stream is reliable on its own, so a lost packet only stalls the one stream it belonged to — plus the handshake merges transport and TLS setup, often needing zero extra round trips on a repeat visit. |
Pick a protocol version, then try the packet-loss toggle to see why HTTP/2's shared connection is also its weakness, and why HTTP/3 fixes it differently than HTTP/1.1 did.
The request line (method, path, version), then headers (one per line, Name: value), then a blank line, then an optional body (empty for a GET).
The status line (version, status code, reason phrase), then headers, then the body — here, the actual HTML.
| Code | Meaning | Category |
|---|---|---|
| 101 | Switching Protocols — used by the WebSocket upgrade below | 1xx Informational |
| 200 | OK — the standard success response | 2xx Success |
| 201 | Created — a new resource was created (common after POST) | 2xx Success |
| 301 | Moved Permanently — update your bookmarks/links | 3xx Redirection |
| 304 | Not Modified — your cached copy is still good, no body sent | 3xx Redirection |
| 400 | Bad Request — the client sent something malformed | 4xx Client Error |
| 401 | Unauthorized — you need to authenticate first | 4xx Client Error |
| 403 | Forbidden — you're authenticated, but not allowed | 4xx Client Error |
| 404 | Not Found | 4xx Client Error |
| 429 | Too Many Requests — rate limited | 4xx Client Error |
| 500 | Internal Server Error — the server broke, not your fault | 5xx Server Error |
| 503 | Service Unavailable — server is overloaded or down for maintenance | 5xx Server Error |
Plain HTTP can't have the server speak first — it can only reply to a request. That's fine for pages, but useless for a chat app where the server needs to push a message the instant it arrives. WebSocket solves this by upgrading a single HTTP connection into a persistent, full-duplex pipe.
gRPC lets services call each other like local functions, serializing arguments with Protocol Buffers and riding on HTTP/2's multiplexed streams. Because it's built on real streams rather than one-shot request/response, it supports four calling patterns:
One request, one response — like a normal function call.
One request, a stream of responses — e.g. subscribing to price updates.
A stream of requests, one final response — e.g. uploading chunks of a file.
Both sides stream independently and simultaneously — e.g. a live voice-translation service.
101 Switching Protocols — a WebSocket server that's down looks like an HTTP server that's down.