Modern HTTP (2 & 3)
HTTP/1.1 was simple but slow for pages made of many small objects. HTTP/2 and HTTP/3 keep the same methods, headers, and status codes - the same semantics - but change how requests travel over the wire.
Pick a version and watch the same four requests schedule on a timeline:
HTTP/1.1: one at a time
On a persistent HTTP/1.1 connection, requests are sent and answered in order. A slow or large response stalls everything queued behind it. This is head-of-line (HoL) blocking. Browsers worked around it by opening several parallel TCP connections per site, but that wastes resources and each connection still blocks internally.
HTTP/2: multiplexed streams
HTTP/2 introduces streams: many independent request/response exchanges share one TCP connection, and their data is interleaved as small frames. Now a slow response no longer blocks the others at the HTTP layer - they all make progress over the single connection.
But there is a catch. Everything still rides on one TCP connection, and TCP delivers bytes strictly in order. If one packet is lost, TCP holds back all streams until it is retransmitted - HoL blocking has moved down into the transport layer.
HTTP/3: QUIC over UDP
HTTP/3 fixes that by dropping TCP. It runs over QUIC, a transport built on UDP that provides its own streams with independent delivery. A lost packet stalls only the stream it belonged to; the others keep flowing. QUIC also folds the transport and encryption handshakes together, so connections set up faster.
| Version | Transport | Concurrency | HoL blocking |
|---|---|---|---|
| HTTP/1.1 | TCP | One request per connection | At the HTTP layer |
| HTTP/2 | TCP | Multiplexed streams, one connection | Moved to TCP |
| HTTP/3 | QUIC / UDP | Independent streams | Eliminated |
Worked example: loading a page with 30 objects
Say a page needs 30 small objects (CSS, JS, icons) from one origin, and the RTT to that server is 80 ms. Browsers cap HTTP/1.1 at roughly 6 parallel connections per origin, so the 30 requests queue up in 30 / 6 = 5 waves, each wave costing one RTT: 5 * 80 ms = 400 ms of pure round-trip time before the last object even starts downloading, on top of a first connection-setup RTT.
HTTP/2 multiplexes all 30 requests over a single connection, so they all go out together after one setup RTT: roughly 80 ms (plus one RTT for the TCP handshake, and another for TLS if it is not resumed) instead of 400 ms+. The gap only grows with more objects or a longer RTT - it is exactly why HTTP/2 felt so much faster on mobile networks, where 80-150 ms RTTs were common.
HTTP/2's fix is incomplete in practice. Because every stream still rides the same TCP connection, a single lost packet stalls all 30 multiplexed streams until TCP retransmits it - the classic "HTTP/2 is slower than HTTP/1.1 on a lossy Wi-Fi network" complaint. Teams that saw this in production either fell back to HTTP/1.1-style connection sharding for flaky clients, or moved to HTTP/3/QUIC, whose per-stream loss recovery does not have this problem.
The application-visible model - methods, headers, status codes - is identical across all three. What changes is the plumbing underneath, and that plumbing is where the latency wins come from.