HTTP/1.1 vs HTTP/2 vs HTTP/3: what actually changed
Three versions of HTTP carry today's web, and each exists to fix a specific bottleneck in the one before. HTTP/1.1 gave us the durable request and response model but handles one response at a time per connection. HTTP/2 multiplexed many exchanges over one TCP connection. HTTP/3 replaced TCP itself with QUIC over UDP, because TCP turned out to be the last bottleneck left. The semantics you know, methods, status codes, headers, are identical across all three; what changes is how bytes move.
You can see which version any site negotiates in your browser's DevTools Network tab, via the Protocol column (http/1.1, h2, h3).
HTTP/1.1: one lane per connection
HTTP/1.1, standardized in 1997, is text over TCP: readable requests and responses, exactly what you see when you run curl with -v. Its longevity is deserved, but it has a structural flaw: one connection carries one response at a time. A page needing 50 resources must fetch them largely in sequence.
The workarounds defined an era of web performance folklore:
- Six parallel connections per host, the browsers' compensation, each costing its own TCP and TLS handshake.
- Domain sharding, spreading assets across
cdn1.,cdn2.to trick browsers into more connections. - Bundling and spriting, concatenating files and stitching icons into one image, purely to reduce request count.
If a response stalls, everything queued behind it on that connection waits. This is head of line blocking, and the next two versions are successive attempts to kill it.
HTTP/2: many lanes, one road
HTTP/2 (2015, built on Google's SPDY) keeps TCP but changes the format from text to binary frames, and with that gains multiplexing: any number of requests and responses interleave simultaneously over one connection. The 50 resource page now flows over a single connection with no queueing at the HTTP layer. Sharding and bundling flipped from best practice to anti-pattern more or less overnight.
Two other wins ship with it:
- Header compression (HPACK). HTTP/1.1 resends near identical headers, cookies especially, with every request; HPACK reduces repeats to a few bytes.
- Prioritization, letting the browser signal that render blocking CSS matters more than a footer image.
In practice HTTP/2 is ubiquitous: virtually every CDN and major site negotiated it years ago, always over TLS in browsers.
But one blocking problem survived, one layer down. TCP guarantees ordered delivery of a single byte stream: lose one packet, and TCP holds back everything after it until the retransmission arrives, including bytes belonging to streams that lost nothing. HTTP/2 removed head of line blocking at the HTTP layer and inherited it back from TCP. On clean networks this is invisible; on lossy WiFi and cellular, a single lost packet stalls every stream at once, and HTTP/2 can actually degrade below HTTP/1.1's six independent connections.
HTTP/3: replace the road
If TCP's ordering guarantee is the problem, the fix is radical by necessity: stop using TCP. HTTP/3 (standardized 2022) runs over QUIC, a transport protocol built on UDP that reimplements TCP's reliability per stream instead of per connection. A lost packet now stalls only the stream it belonged to; every other stream keeps flowing. That is the core fix, and everything else about QUIC is a bonus round:
- Faster setup. QUIC folds transport and TLS 1.3 into a single handshake, one round trip instead of two or three, and zero for resumed connections.
- Connection migration. QUIC connections are identified by an ID, not the IP/port tuple, so walking from WiFi to cellular keeps the connection alive instead of resetting every transfer.
- Encryption is mandatory and deeper: even most transport metadata is encrypted, which also prevents middleboxes from ossifying the protocol.
The practical caveat: QUIC lives in userspace and UDP, so some corporate firewalls block or throttle it. Discovery handles this gracefully: servers advertise HTTP/3 via an Alt-Svc header or DNS HTTPS records, browsers try it, and fall back to HTTP/2 silently when the path is hostile. You lose nothing by enabling it.
Side by side
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Year | 1997 | 2015 | 2022 |
| Transport | TCP | TCP | QUIC over UDP |
| Format | Text | Binary frames | Binary frames |
| Multiplexing | No | Yes | Yes |
| Head of line blocking | HTTP and TCP layer | TCP layer only | Neither |
| Header compression | No | HPACK | QPACK |
| TLS | Optional | Required by browsers | Built into QUIC |
| Setup round trips (with TLS) | 2–3 | 2–3 | 1, or 0 resumed |
What should you actually do?
If you deploy behind a modern CDN or platform, Cloudflare, Vercel, Fastly, and peers, HTTP/2 and HTTP/3 are typically already on, and this whole post is a checkbox you can verify rather than a project. Self hosting needs recent software: nginx supports HTTP/3 from 1.25, Caddy enables it by default.
Two version specific habits worth keeping:
- Stop bundling aggressively. With multiplexing, many small cacheable files beat one giant blob, because one changed module no longer invalidates the whole bundle in caches.
- Trim cookies anyway. Compression helps repeated headers, but the first request still carries them in full.
To check a site's setup end to end, response headers including Alt-Svc are visible in the HTTP Headers tool, your TLS configuration in the SSL Checker, and the protocol actually negotiated in DevTools. The versions differ in transport, but everything they carry, the methods, the status codes, the caching rules, behaves identically, which is exactly why the upgrades could happen without breaking the web.