HTTP/1.1 vs HTTP/2 vs HTTP/3
Three decades of HTTP evolution: framing, multiplexing, header compression, push, and the move to QUIC.
A short history
HTTP/0.9 (1991): one line, GET / followed by HTML response. No headers. No status codes.
HTTP/1.0 (1996): headers, status codes, content types. One TCP connection per request, closed after response.
HTTP/1.1 (1997, RFC 2068): persistent connections (keep-alive), pipelining, chunked transfer, Host header, more cache controls. Still text. This was the web for 18 years.
HTTP/2 (2015, RFC 7540, now 9113): binary framing, stream multiplexing, header compression. Same semantics as 1.1, completely different wire format.
HTTP/3 (2022, RFC 9114): same semantics again, transport changed from TCP to QUIC (RFC 9000).
The semantics (methods, status codes, headers) have been remarkably stable. What changes is how bytes go on the wire.
HTTP/1.1: the text protocol
Wire format is human-readable.
GET /api/users HTTP/1.1
Host: api.example.com
User-Agent: curl/8.0
Response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
{"users": [{"id": 1, "name": "yash"}]}
Connection model
A TCP connection carries one request-response at a time. After the response, the connection can be reused (keep-alive) or closed.
Pipelining was specified: send multiple requests without waiting for responses. In practice, broken middleboxes and head-of-line blocking made pipelining unusable. Browsers never enabled it.
Head-of-line blocking at L7
Request 2 must wait for response 1 to fully arrive before its response can begin. Even with pipelining, a slow response 1 blocks response 2.
Browser workaround: parallel connections
Browsers open up to 6 TCP connections per origin to fake concurrency. Each connection is its own TLS handshake, its own slow-start, its own buffer. This is wasteful but it works.
Domain sharding
To get more than 6 parallel requests, sites would shard assets across subdomains (cdn1.example.com, cdn2.example.com). Each gets its own 6 connections. This is an HTTP/1.1-era trick; on HTTP/2 it actively hurts because you lose connection coalescing.
HTTP/2: binary, multiplexed, compressed
HTTP/2 wraps HTTP semantics in a binary frame format and multiplexes many logical streams onto one TCP connection.
Framing
Every transmission is a frame: 9-byte header + payload. Frame types include DATA, HEADERS, SETTINGS, PUSH_PROMISE, RST_STREAM, WINDOW_UPDATE, PING, GOAWAY.
Each frame carries a stream ID. The connection multiplexes many streams; frames from different streams interleave.
Stream IDs: odd from client, even from server. New streams use the next higher ID.
HPACK header compression
HTTP/1.1 headers are repetitive: User-Agent, Cookie, Accept rarely change between requests. HPACK (RFC 7541) uses a shared dynamic table on both sides. After the first request, headers compress to a few bytes of references.
Typical compression: 80-90% on header sizes. For an API with a 2 KB cookie sent on every request, this is huge.
HPACK has its own attack surface: CRIME-style attacks if you mix attacker-controlled and secret data in the same headers. RFC 7541 addresses some, careful implementation closes the rest.
Flow control
Per-stream and per-connection flow control via WINDOW_UPDATE frames. A receiver can throttle a noisy stream without blocking others.
Server push (deprecated)
Server can preemptively send resources the client will need: send /index.html and push /style.css before the client parses HTML and asks. In theory, saves a round trip.
In practice, it required careful cache awareness (do not push what the client already cached) and was easy to get wrong. Chrome removed support in 2022. 103 Early Hints replaced it.
Prioritization
HTTP/2 had a complex priority tree. Implementations diverged wildly; few got it right. HTTP/2 priorities are now considered broken and being replaced by RFC 9218 (Extensible Prioritization Scheme).
The TCP HoL problem
HTTP/2 multiplexes at the application layer, but TCP delivers bytes in order. One lost packet stalls every concurrent stream until the retransmit arrives.
On a fast clean network, multiplexing wins. On a lossy mobile network, HTTP/1.1 with 6 parallel TCP connections can actually beat HTTP/2 because each connection has its own packet loss recovery.
This is the central limitation that motivated HTTP/3.
HTTP/3: HTTP over QUIC
HTTP/3 uses the same HTTP semantics and a similar binary framing as HTTP/2, but runs over QUIC (UDP-based) instead of TCP.
QUIC primer
QUIC provides:
- Reliable, ordered, congestion-controlled streams (like TCP).
- Per-stream sequencing (unlike TCP, so no HoL across streams).
- Built-in TLS 1.3, encryption mandatory.
- 0-RTT connection resumption.
- Connection migration: a connection survives IP changes (Wi-Fi to LTE).
QUIC lives in userspace, not the kernel. This means new features ship with app updates, not OS upgrades. Cloudflare, Google, Facebook all run their own QUIC stacks tuned for their workloads.
Why over UDP
The internet's middleboxes (NATs, firewalls, load balancers) drop or mangle unknown L4 protocols. You cannot ship a new L4 in 2026. QUIC uses UDP as a vehicle because UDP is well-understood and unfiltered.
QPACK instead of HPACK
HPACK is order-dependent: the decoder must process headers in order. On QUIC where streams are independent, this would re-introduce HoL. QPACK splits the dynamic table updates and references into separate streams, allowing out-of-order processing without HoL.
0-RTT resumption
On a resumed connection, the client can include data in the first packet (using TLS 1.3 session tickets). The server can respond in 1 RTT total. On a cold connection, QUIC negotiates TLS and transport in parallel: 1 RTT.
Versus TCP+TLS 1.3: 1 RTT for TCP + 1 RTT for TLS = 2 RTTs. QUIC: 1 RTT (or 0 on resume).
Connection migration
A QUIC connection is identified by a Connection ID, not by the 5-tuple. When your phone switches from Wi-Fi to cellular, the IP changes but the QUIC connection survives. TCP would have to re-establish.
Trade-offs
- CPU cost: encryption per packet, userspace processing. ~2x CPU vs TCP.
- UDP not always allowed: some corporate networks block UDP except for DNS. Browsers fall back to HTTP/2.
- Newer, less battle-tested middleboxes than TCP.
When to use which
- HTTP/1.1: low-frequency APIs, debugging, tools that need text wire format. Curl scripts. Internal services where you do not care.
- HTTP/2: most modern web apps. Default for most CDNs.
- HTTP/3: high-latency or lossy networks (mobile), large pages with many resources, sites where connection migration matters.
Most CDNs (Cloudflare, Fastly, Akamai) support all three and negotiate the best version with each client.
What you actually configure
Server:
- Nginx:
listen 443 ssl http2;andlisten 443 quic reuseport;for HTTP/3. - Cloudflare: toggle in dashboard.
Browser: HTTP/3 needs the server to advertise via Alt-Svc header or DNS HTTPS RR. Browsers cache the upgrade hint and try QUIC on next visit.
Performance ranking on typical workloads
- HTTP/3 over QUIC on a resumed connection: 0 RTT, no HoL.
- HTTP/2 on a warm TCP connection: header compression, multiplexing, no setup.
- HTTP/1.1 with keep-alive and 6 parallel connections: serviceable.
- HTTP/1.1 cold connections: 3 RTTs per resource (TCP + TLS + request).
Common mistakes
- Shipping with domain sharding on HTTP/2: defeats connection coalescing, hurts performance.
- Relying on server push: removed from Chrome, use 103 Early Hints.
- Assuming HTTP/3 just works: requires UDP egress, Alt-Svc, and correct CDN config.
- Forgetting HPACK state is per-connection: if your load balancer terminates and re-establishes, you pay full header cost again.
Numbers to memorize
- HTTP/1.1 parallel connections per origin: 6 (browser default).
- HTTP/2 streams per connection: up to 100 concurrent by default, can negotiate higher.
- HPACK header compression: 80-90% reduction.
- HTTP/3 0-RTT savings: 1 RTT (50-200 ms on real networks).
- Frame header size: 9 bytes (HTTP/2) or variable (HTTP/3).
Learn more
- Paper
- Paper
- PaperRFC 9113: HTTP/2IETF
- PaperRFC 9114: HTTP/3IETF
- DocsHigh Performance Browser NetworkingIlya Grigorik
- ArticleCloudflare: HTTP/3 explainedCloudflare