HTTP/1.0 → HTTP/1.1 → HTTP/2 → HTTP/3¶
HTTP/1.0: One TCP connection per request. Every request pays for TCP + TLS handshake. Terrible for pages with many resources (images, scripts, CSS).
HTTP/1.1: Persistent connections (keep-alive). Multiple requests reuse one TCP connection. Application-level HOL blocking: server must respond in order — slow response 1 blocks responses 2 and 3. Browsers work around this with 6–8 parallel connections per origin.
HTTP/2: Binary framing + stream multiplexing: multiple independent streams on one TCP connection. Slow response on stream 3 does not block stream 5 — frames interleave on the wire. Eliminates application-level HOL blocking. TCP-level HOL remains: one lost TCP packet blocks all streams. HPACK header compression reduces header overhead by ~85%.
HTTP/3 (QUIC): Runs over UDP. QUIC implements per-stream reliability — a lost packet on stream 3 only blocks stream 3. Eliminates TCP-level HOL. 0-RTT resumption for returning clients. Connection migration: client IP change (WiFi → cellular) doesn't break the connection (identified by connection ID, not 4-tuple).
HOL blocking at each layer: - HTTP/1.1: application HOL + no multiplexing - HTTP/2: application HOL fixed; TCP HOL remains - HTTP/3: both fixed
Worked example — loading a page with 30 assets, RTT = 50ms: on HTTP/1.1 with 6 parallel connections per origin, 30 requests split into 5 waves of 6 (one wave per connection-slot cycle); each wave pays roughly one RTT for its round trip, so the page finishes in about 5 × 50ms = 250ms of pure request/response latency, before counting actual transfer time — and that's with the 6-connection workaround; without it, one connection serialized would be 30 × 50ms = 1.5s. On HTTP/2, all 30 requests multiplex onto the single TCP connection as interleaved frames and effectively go out together — total latency collapses to roughly one RTT (~50ms) plus transfer time, because the server can start responding to all 30 as soon as it receives them, not one-at-a-time. The place HTTP/2 doesn't fully win: if that single TCP connection loses one packet, TCP itself won't deliver any of the 30 streams' data past that point until the lost packet is retransmitted — every stream stalls together, even though the HOL blocking that HTTP/1.1 had (one slow response blocking others) is gone. HTTP/3 fixes exactly this remaining case: it runs each stream with independent reliability over QUIC/UDP, so a packet lost on the data for asset #7 stalls only asset #7 — the other 29 keep flowing.