Engineering Notes
Home/Networking & Web/HTTP/HTTP Versions
Networking & Web · HTTP

HTTP Versions

HTTP/1.1, HTTP/2 and HTTP/3 carry the same semantics over very different transports. Watch six parallel connections give way to multiplexed streams, then see how QUIC stops one lost packet from stalling every stream.

🌐 Networking & Web● Intermediate⏱ 6 min read▶ 2 interactive🛠️ 2 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • HTTP/1.0 → 1.1: keep-alive and its limits
  • HTTP/2: binary frames, streams, HPACK
  • HTTP/3: QUIC over UDP
  • Comparing versions and avoiding outdated optimizations

📚 Before you start

1HTTP/0.9 and HTTP/1.0 (Historical)

HTTP/0.9 (1991) was a single GET /path line, no headers, no status code, connection closed immediately after the response — enough to fetch a document and nothing else. HTTP/1.0 (1996) added headers, status codes, and other methods, but opened a new TCP connection per request by default, which is expensive (see the TCP handshake cost in the HTTP Fundamentals topic).

2HTTP/1.1 (1997) — Still Widely Used

HTTP/1.1 made connections persistent by default (Connection: keep-alive) so multiple requests could reuse one TCP connection, and added chunked transfer encoding, Host header (required — enables name-based virtual hosting on the same IP), and conditional requests (If-None-Match, etc.).

The unsolved problem: head-of-line (HOL) blocking on the connection. HTTP/1.1 is fundamentally a serial protocol on a given connection — the server must send complete response N before response N+1 can begin, even if response N+1 finished processing first. Browsers work around this by opening multiple parallel TCP connections per host (historically 6) — which just shifts the cost to more handshakes and more contention, it doesn't remove the blocking.

text
Connection 1: [--- Request A ---][--- Response A ---][--- Request B ---][--- Response B ---]
              (B cannot start until A's response is fully sent, on THIS connection)

3HTTP/2 (2015) — Binary Framing and Multiplexing

HTTP/2 keeps the same semantics (same methods, status codes, headers) but changes the wire format entirely: everything is split into small, length-prefixed, binary frames, and frames from many concurrent logical streams are interleaved over a single TCP connection.

text
One TCP connection:
  [stream 1 frame][stream 3 frame][stream 1 frame][stream 5 frame][stream 3 frame]...

Each stream carries one request/response pair and has an odd (client-initiated) or even (server-initiated, for now-deprecated Server Push) integer ID. The server can interleave frames from streams 1, 3, and 5 in any order — a large, slow response on stream 3 no longer blocks a small, fast response on stream 1 from being delivered first. Headers are additionally compressed with HPACK, which maintains a shared dynamic table between client and server so repeated header values (cookies, user-agent) across requests on the same connection are sent as small index references instead of full text.

One HTTP/1.1 connection serializes responses; one HTTP/2 connection interleaves frames from multiple concurrent streams
One HTTP/1.1 connection serializes responses; one HTTP/2 connection interleaves frames from multiple concurrent streams

4HTTP/3 (2022) — QUIC over UDP

HTTP/2 solved HOL blocking at the HTTP layer but not underneath it: all streams still share one TCP connection, and TCP itself guarantees in-order byte delivery — if a single packet is lost, TCP holds up every stream's data behind it until that packet is retransmitted, even though the lost bytes might belong to only one HTTP/2 stream. This is transport-level head-of-line blocking, and no amount of HTTP-layer cleverness can fix it while running on TCP.

HTTP/3 solves this by dropping TCP entirely and running over QUIC, a transport protocol built on UDP. QUIC implements its own reliability and multiplexing, but critically, each QUIC stream is independently ordered and independently recovers from loss — a lost packet only blocks the one stream it belonged to, not the others sharing the connection. QUIC also folds the transport and TLS 1.3 handshakes together (often 1 round trip total, sometimes 0 for resumed connections), versus HTTP/2's separate TCP + TLS handshakes.

5Version Comparison

HTTP/1.1HTTP/2HTTP/3
Formattextbinary framesbinary frames (QUIC)
TransportTCPTCPUDP (QUIC)
Multiplexingno (parallel connections instead)yes, single connectionyes, single connection
HOL blockingyes (per-connection, HTTP-level)transport-level only (TCP)effectively eliminated
Header compressionnoneHPACKQPACK
Handshake costTCP (+TLS if HTTPS)TCP (+TLS if HTTPS)QUIC handshake (combines transport+TLS)
Under the HoodFrames, Streams, and Why QUIC Changes the Game▾

HTTP/2 frame types are real, typed units

An HTTP/2 connection is a sequence of frames, each with a 9-byte header: length, type, flags, and a stream ID. Common types: HEADERS (start of a request/response's headers, HPACK-compressed), DATA (body bytes), SETTINGS (connection-wide parameters like max concurrent streams), WINDOW_UPDATE (flow control credit), RST_STREAM (cancel one stream without killing the connection — impossible in HTTP/1.1, where cancelling a request means closing and reopening the whole TCP connection). Stream IDs let an endpoint demultiplex incoming frames back into the right logical request/response, purely from a field in each frame header — no separate connections needed per request.

Why TCP's guarantee becomes a liability

TCP's core promise — bytes arrive at the application in the exact order they were sent, with any gap causing everything after it to wait — is exactly right for a single stream of data, but actively wrong once you've multiplexed unrelated streams onto one TCP connection: from TCP's point of view there is only one byte stream, so a lost packet belonging to HTTP/2 stream 5 stalls delivery of already-arrived bytes belonging to stream 1 and 3 as well, because TCP won't hand any of it up to the application until the gap is filled. QUIC instead multiplexes at the transport layer itself — each QUIC stream has its own independent sequence numbering — so a lost packet only stalls the one stream whose data it carried.

QUIC's handshake collapses two round trips into one (or zero)

A first-time HTTPS connection over TCP needs: 1 RTT for the TCP handshake, then 1–2 RTTs for the TLS handshake, before the first HTTP request byte can even be sent. QUIC integrates TLS 1.3 directly into its own handshake, typically completing connection setup and the cryptographic handshake in a single round trip; if the client has connected to that server recently, QUIC supports 0-RTT resumption — sending application data in the very first packet, before the handshake even finishes (with a defined security tradeoff: 0-RTT data is replayable, so it's typically restricted to idempotent requests).

⚠️ Common Pitfalls

  1. Assuming HTTP/2 automatically makes an app faster — it removes HTTP-level HOL blocking and reduces connection overhead, but a slow backend or a network with real packet loss still bottlenecks it (and packet loss specifically is where HTTP/2 still suffers from TCP's transport-level HOL blocking).
  2. Still domain-sharding assets under HTTP/2 — splitting assets across img1.example.com, img2.example.com was an HTTP/1.1 workaround for the 6-connections-per-host limit; under HTTP/2 it actively hurts, since it defeats multiplexing and forces multiple separate connections/handshakes instead of one.
  3. Confusing HTTP/2's HPACK header compression with body compression — HPACK only compresses headers; response body compression (gzip/brotli) is a completely separate mechanism, covered in the Caching and Compression topic.
  4. Assuming HTTP/3 needs no fallback — QUIC runs over UDP, which some corporate firewalls and older middleboxes block outright, so real deployments still need to advertise HTTP/3 (via the Alt-Svc header) and gracefully fall back to HTTP/2 over TCP.

📋 Quick Reference

text
HTTP/1.1: text, TCP, serial-per-connection, workaround = multiple connections
HTTP/2:   binary frames, TCP, multiplexed streams, HPACK headers
HTTP/3:   binary frames, QUIC/UDP, per-stream loss recovery, QPACK headers

HOL blocking fixed at HTTP layer:      HTTP/2, HTTP/3
HOL blocking fixed at transport layer: HTTP/3 only (needs UDP/QUIC, not TCP)

🧠 Key takeaways

  • HTTP/1.1: keep-alive, but one request at a time per connection → 6 connections per host.
  • HTTP/2: binary framing, multiplexed streams on one TCP connection, HPACK; still suffers TCP HOL on loss.
  • HTTP/3: QUIC over UDP, per-stream reliability, 1-RTT/0-RTT setup, connection migration.
  • Drop HTTP/1 hacks (sharding, sprites) on h2/h3; server push is effectively dead.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1What changed from HTTP/1.1 to HTTP/2?core▾

HTTP/2 is binary, multiplexes many requests over one connection as streams, compresses headers (HPACK), and supports stream prioritization. It removes the need for domain sharding and asset concatenation hacks.

Follow-ups interviewers ask
  • What about server push? Mostly abandoned; browsers removed support.
Q2Why was HTTP/3 created?core▾

HTTP/2 over TCP still suffers TCP head-of-line blocking: one lost packet stalls all streams. HTTP/3 runs over QUIC (UDP), with independent streams, faster handshakes (TLS 1.3 integrated, 0-RTT), and connection migration across networks.

Follow-ups interviewers ask
  • What's connection migration? A connection survives a client IP change, e.g. Wi-Fi to mobile.
Q3Does HTTP/2 change your API design?senior▾

Not the semantics (methods, status codes, headers stay the same), but many small requests become cheap, so you needn't bundle aggressively. gRPC relies on HTTP/2 features like streams and trailers.

Follow-ups interviewers ask
  • Load balancing impact? Long-lived multiplexed connections need L7 balancing.
Q4How do clients negotiate HTTP versions?core▾

HTTP/2 via ALPN in the TLS handshake; HTTP/3 via the Alt-Svc header or DNS HTTPS records telling the client the server supports it, after which the client tries QUIC.

Follow-ups interviewers ask
  • Is HTTP/2 used without TLS? Browsers require TLS; h2c exists for internal use.
Q5What problems can HTTP/3 face in practice?core▾

Some networks block or throttle UDP, so clients must fall back to HTTP/2; middleboxes and firewalls may not understand QUIC; and CPU cost for QUIC is higher than kernel TCP.

Follow-ups interviewers ask
  • Who supports it? Major browsers and CDNs (Cloudflare, Fastly, Google).

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. Main improvement of HTTP/2 over 1.1?
Streams.
2. Why does HTTP/2 still suffer head-of-line blocking?
Transport-level HOL.
3. HTTP/3 runs over…
UDP-based.
4. Domain sharding on HTTP/2 is…
One connection is better.