Overview
Each HTTP version was born to fix a specific bottleneck in the previous one. Understanding the progression is a standard system design interview topic — you'll be asked "why is HTTP/2 better than HTTP/1.1" or "when would you use HTTP/3?"
| Version | Year | Key Fix |
|---|---|---|
| HTTP/1.0 | 1996 | Baseline — one request per TCP connection |
| HTTP/1.1 | 1997 | Persistent connections, pipelining |
| HTTP/2.0 | 2015 | Multiplexing, header compression, server push |
| HTTP/3.0 | 2022 | QUIC over UDP, removes transport-layer HOL |
Architecture
HTTP/1.0 ─── one TCP connection per request ──────────────────
Client Server
│── TCP handshake ──────────▶│
│── GET /index.html ────────▶│
│◀─ 200 OK ─────────────────│
│── TCP close ──────────────▶│
│ │
│── TCP handshake (new!) ───▶│ ← NEW connection for every request
│── GET /style.css ─────────▶│
│◀─ 200 OK ─────────────────│
─────────────────────────────────────────────────────────────────
HTTP/1.1 ─── persistent connection (keep-alive) ───────────────
Client Server
│── TCP handshake ──────────▶│
│── GET /index.html ────────▶│
│◀─ 200 OK ─────────────────│
│── GET /style.css ─────────▶│ ← same connection reused
│◀─ 200 OK ─────────────────│
│── GET /app.js ────────────▶│ ← but still SEQUENTIAL
│◀─ 200 OK ─────────────────│ ← HOL blocking if /style.css is slow
─────────────────────────────────────────────────────────────────
HTTP/2.0 ─── multiplexing over single connection ───────────────
Client Server
│── TCP handshake ──────────▶│
│ │
│ Stream 1: GET /index.html ─▶│
│ Stream 2: GET /style.css ──▶│ ← all in parallel
│ Stream 3: GET /app.js ─────▶│ ← on ONE TCP connection
│ │
│◀─ Stream 2: 200 OK ────────│ ← responses out of order
│◀─ Stream 1: 200 OK ────────│
│◀─ Stream 3: 200 OK ────────│
│ │
NOTE: if a TCP packet is lost, ALL streams stall ← TCP HOL
─────────────────────────────────────────────────────────────────
HTTP/3.0 ─── QUIC over UDP (no TCP) ────────────────────────────
Client Server
│── QUIC handshake (1-RTT) ─▶│ ← TLS 1.3 built in
│ │
│ QUIC Stream 1: /index ─────▶│
│ QUIC Stream 2: /style ─────▶│ ← independent streams
│ QUIC Stream 3: /app ───────▶│
│ │
│◀─ Stream 2 response ───────│
│◀─ (Stream 1 packet lost) │ ← only Stream 1 retransmits
│◀─ Stream 3 response ───────│ ← Stream 3 NOT blocked!
Technical Implementation
HTTP/1.1 HOL Problem (Code Illustration)
// HTTP/1.1: browsers open ~6 parallel connections per domain
// but each connection is sequential → head-of-line blocking
// If /api/slow takes 3s, /api/fast must WAIT behind it in the same connection
// Workaround: domain sharding (cdn1.example.com, cdn2.example.com)
// → anti-pattern in HTTP/2 (hurts because it prevents connection reuse)
HTTP/2 Server Push
// Node.js HTTP/2 server push — proactively send CSS with HTML
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({ key, cert });
server.on('stream', (stream, headers) => {
if (headers[':path'] === '/') {
// Push CSS before client asks for it
stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
pushStream.respondWithFile('./style.css');
});
stream.respondWithFile('./index.html');
}
});
HTTP/3 Connection (Node.js via quic package)
// HTTP/3 uses QUIC — built into browsers, enabled via Alt-Svc header
// Server signals HTTP/3 support:
res.setHeader('Alt-Svc', 'h3=":443"; ma=86400');
// Browser connects via QUIC on next request
// Key config: TLS 1.3 required, UDP port 443
Enabling HTTP/2 in nginx
server {
listen 443 ssl http2; # ← add 'http2' flag
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# HTTP/2 push
http2_push /style.css;
http2_push /app.js;
}
Interview Preparation
Q: What is HOL (Head-of-Line) blocking and which HTTP versions have it?
HOL blocking: a slow request blocks all subsequent requests in the same queue.
- HTTP/1.1: one request at a time per connection — slow response blocks everything behind it on that connection.
- HTTP/2: eliminated at the application layer (streams are independent), but a lost TCP packet blocks all streams while waiting for retransmission — TCP-layer HOL.
- HTTP/3: eliminated at both layers. QUIC streams are independent at the transport level — a lost packet only stalls that specific stream.
Q: Why does HTTP/2 use a single TCP connection instead of multiple?
Multiple connections waste resources (3-way handshake, TLS negotiation, separate congestion windows). HTTP/2 multiplexes all streams over one connection — more efficient use of the TCP connection's congestion window, one TLS session, and the server can prioritize stream delivery.
Q: When should you use HTTP/3?
When users are on high-packet-loss networks (mobile, WiFi, cellular). HTTP/3's QUIC handles packet loss per-stream, so a 1% packet loss rate that would stall HTTP/2 streams barely affects HTTP/3. Also useful for applications where connection setup latency matters — QUIC combines the transport and TLS handshakes into 1-RTT (vs TCP's 3-way + TLS = 2-3 RTTs).
Q: What is QUIC?
QUIC (Quick UDP Internet Connections) is a transport protocol built on UDP. It reimplements TCP's reliability features (acknowledgment, retransmission, flow control) at the application layer, plus adds independent stream multiplexing and 0-RTT connection resumption. Developed by Google, standardized as RFC 9000.
Learning Resources
- HTTP/2 Explained — Daniel Stenberg (curl author)
- HTTP/3 Explained — Daniel Stenberg
- High Performance Browser Networking — Ilya Grigorik (free online)
- MDN HTTP Overview
- QUIC RFC 9000