Why the HTTP versions changed

VersionYearWhat changed, and why
0.91991One line, one method (GET), no headers, HTML only. The connection closed after every response.
1.01996Added headers, status codes, other content types — but still opened a brand new TCP connection (and did a fresh handshake) for every single request.
1.11997Added 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.
22015Binary 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.
32022Runs 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.

See it happen: loading 6 assets under each version

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.

Connection setup Transferring Blocked

Anatomy of an HTTP request & response

GET /articles/networking HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 Accept: text/html Accept-Encoding: gzip, br Connection: keep-alive

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).

HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 4096 Cache-Control: max-age=3600 Set-Cookie: session=abc123; HttpOnly <html>...</html>

The status line (version, status code, reason phrase), then headers, then the body — here, the actual HTML.

Status code reference

CodeMeaningCategory
101Switching Protocols — used by the WebSocket upgrade below1xx Informational
200OK — the standard success response2xx Success
201Created — a new resource was created (common after POST)2xx Success
301Moved Permanently — update your bookmarks/links3xx Redirection
304Not Modified — your cached copy is still good, no body sent3xx Redirection
400Bad Request — the client sent something malformed4xx Client Error
401Unauthorized — you need to authenticate first4xx Client Error
403Forbidden — you're authenticated, but not allowed4xx Client Error
404Not Found4xx Client Error
429Too Many Requests — rate limited4xx Client Error
500Internal Server Error — the server broke, not your fault5xx Server Error
503Service Unavailable — server is overloaded or down for maintenance5xx Server Error

WebSocket: escaping request/response

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: RPC over HTTP/2

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:

Unary

client ──req──▶ server
client ◀──res── server

One request, one response — like a normal function call.

Server streaming

client ──req──▶ server
client ◀──res── server
client ◀──res── server
client ◀──res── server

One request, a stream of responses — e.g. subscribing to price updates.

Client streaming

client ──req──▶ server
client ──req──▶ server
client ──req──▶ server
client ◀──res── server

A stream of requests, one final response — e.g. uploading chunks of a file.

Bidirectional streaming

client ──req──▶ server
client ◀──res── server
client ──req──▶ server
client ◀──res── server

Both sides stream independently and simultaneously — e.g. a live voice-translation service.

Common gotchas