HTTP Versions
Date: 2026-08-17
Three wire formats for the same semantics. Each version fixed the bottleneck the previous one created, and each fix invalidated an optimisation that was best practice under the old one — which is why so much performance advice is quietly out of date.
HTTP/1.1, HTTP/2 and HTTP/3 change how messages are transmitted, not what they mean. A GET returning 200 means the same thing in all three — HTTP Semantics.
What each changed
HTTP/1.1 1997
text protocol, one request at a time
per connection
→ browsers opened ~6 connections
per host to fake parallelism
HTTP/2 2015
binary, MULTIPLEXED over one connection
header compression, server push
→ many requests share one connection
← still TCP, so still head-of-line
blocking
HTTP/3 2022
same multiplexing, over QUIC (UDP)
→ per-stream loss recovery
→ handshake folded into connection
setup
The problem each was solving
HTTP/1.1’s bottleneck was request count. One request at a time per connection, six connections per host, so the seventh resource waits.
6 connections, 30 resources
→ five waves of requests
HTTP/2 multiplexed them — all thirty in flight on one connection, interleaved.
But TCP guarantees order, so a single lost packet blocks every stream sharing the connection until it’s retransmitted — TCP and UDP:
HTTP/1.1 6 connections, loss affects 1
HTTP/2 1 connection, loss affects ALL
On a clean network HTTP/2 wins comfortably. On a lossy one it could be worse than HTTP/1.1 — which is the problem HTTP/3 solved by moving to QUIC and recovering losses per stream.
The optimisations that HTTP/2 made obsolete
This is the practically important part, because the advice persists:
HTTP/1.1 BEST PRACTICE UNDER HTTP/2+
concatenate all JS harmful — one
into one bundle change invalidates
the whole cache
CSS sprites unnecessary, and
they break caching
granularity
domain sharding actively harmful —
(assets on splits one connection
static1., static2.) into several, each
paying handshake and
slow start
inline small assets usually harmful —
as data URIs uncacheable, and
inflates the HTML
All four were correct in 2014 and are wrong now. If you inherit a build doing them, undoing them is often a free performance win — Code Splitting.
Server push, which failed
HTTP/2 could push resources the client hadn’t asked for. In practice it pushed things the client already had cached, wasting bandwidth, and it was hard to use correctly.
It has been removed from Chrome and is effectively dead. 103 Early Hints is the replacement — the server sends hints about what to preload while it’s still generating the response, and the client decides. Same benefit, client keeps control.
[CHECK: current browser and CDN support for 103 Early Hints before relying on it.]
What you actually control
You don’t choose the version — the client and server negotiate the best both support. What you control:
- Enabling HTTP/2 and /3 on your server or CDN. Usually a toggle, and usually already on with any modern CDN
- HTTPS, which is a hard prerequisite for both — TLS and HTTPS
- Not fighting the protocol with obsolete 1.1-era optimisations
- Reducing distinct hostnames, which is now a straightforward win rather than a tradeoff — Third-Party Scripts
Honest expectations
HTTP/3’s gains are concentrated on poor connections — mobile, congested, high-loss. On a fast wired link the difference is small, which is why synthetic benchmarks underwhelm while real-user metrics improve.
That distribution is the point. The users on bad connections are the ones whose experience has most room to improve, and they’re systematically underrepresented in whatever you test on — Percentiles in Performance.