Suggest an editImprove this articleRefine the answer for “HTTP/2 vs HTTP/3: protocol evolution”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**HTTP/2 vs HTTP/3**: HTTP/2 multiplexes streams over a single TCP connection, but a lost TCP packet blocks all streams. HTTP/3 uses QUIC over UDP, where each stream recovers independently. ```bash curl -I --http2 https://site.com # → HTTP/2 200 curl -I --http3 https://site.com # → HTTP/3 200 ``` **Key difference:** HTTP/3 eliminates TCP packet-level head-of-line blocking via QUIC, which matters most on mobile networks with packet loss above 1%.Shown above the full answer for quick recall.Answer (EN)Image**HTTP/2** multiplexes requests over a single TCP connection; **HTTP/3** replaces TCP with QUIC over UDP and fixes the packet-level blocking that TCP cannot solve. ## Theory ### TL;DR - HTTP/2 is like a shared highway: all cars move faster together, but one broken car blocks every lane. HTTP/3 gives each car its own road. - Main difference: HTTP/2 still has TCP packet-level head-of-line (HOL) blocking. HTTP/3 uses QUIC, where each stream recovers independently. - On stable networks, HTTP/2 and HTTP/3 perform similarly. At 3% packet loss, HTTP/3 holds 90%+ throughput while HTTP/2 drops 40%. - Decision rule: intranet or stable LAN → HTTP/2. Mobile, video streaming, global traffic → HTTP/3. ### Quick example ```bash # Check which protocol your server is using curl -I --http2 https://yourdomain.com # → HTTP/2 200 curl -I --http3 https://yourdomain.com # → HTTP/3 200 # In Chrome DevTools: Network tab → Protocol column # Look for "h2" or "h3" ``` ```javascript // Node.js: detect active protocol per request app.use((req, res, next) => { const v = req.httpVersion; const proto = v >= '3' ? 'h3' : v === '2.0' ? 'h2' : 'h1'; console.log(`${proto}: ${req.path}`); next(); }); // Output: h3: /api/feed (when HTTP/3 is active) ``` Protocol negotiation happens via ALPN during the TLS handshake. The browser and server agree on `h2` or `h3` before sending any request data. ### Key difference: HOL blocking at the TCP layer HTTP/2 solved the per-request HOL blocking of HTTP/1.1 by multiplexing streams on one TCP connection. But TCP still guarantees strict byte order. When one packet is lost, every stream on that connection waits, even streams with nothing to do with the lost packet. That is TCP packet-level HOL blocking. HTTP/3 puts multiplexing inside QUIC, which runs over UDP. QUIC assigns each stream its own loss recovery. A lost UDP datagram stalls only the stream it belongs to. The rest keep flowing. On a 5% packet loss network, HTTP/2 drops around 40% in throughput; HTTP/3 holds above 90%. ### When to use - Stable LAN or intranet → HTTP/2. TCP works fine, HOL rarely triggers, setup is simpler. - Mobile users with variable networks → HTTP/3. QUIC recovers from loss per stream, not per connection. - Video streaming → HTTP/3. YouTube reduced mobile rebuffering 10% with h3. Netflix cut start-play time 25%. - APIs with small payloads → HTTP/2. HPACK header compression shines here, and QUIC adds 20% CPU overhead on ARM servers for little gain. - Behind Cloudflare or a major CDN → HTTP/3 is already on. No extra work needed. - Corporate firewalls → HTTP/2 fallback. UDP 443 is often blocked, and QUIC falls back to h2 automatically. ### Comparison table | Feature | HTTP/2 | HTTP/3 | |---|---|---| | **Transport** | TCP | QUIC over UDP | | **Multiplexing** | Yes, streams on 1 TCP connection | Yes, fully independent streams | | **HOL blocking** | Stream-level solved, packet-level remains | None - per-stream recovery | | **Handshake** | TCP + TLS (2-3 RTTs) | QUIC + TLS 1.3 (1 RTT, 0-RTT on resume) | | **Header compression** | HPACK | QPACK | | **Connection migration** | No - tied to IP+port | Yes - Connection ID survives network change | | **Encryption** | Optional (TLS) | Required (TLS 1.3 built in) | | **Browser support (2025)** | 98% | 92% (Chrome 91+, Firefox 88+, Safari 14+) | | **CPU cost** | Baseline | ~20% more on ARM for QUIC overhead | | **Best for** | Stable networks, quick setup | Mobile, streaming, lossy networks | ### How it works internally The browser sends an ALPN extension during the TLS handshake listing supported protocols (`h2`, `h3`). The server picks one and responds. For HTTP/2, Chromium parses incoming bytes as HPACK-compressed HEADERS and DATA frames, mapping each to a stream ID on the TCP socket. For HTTP/3, the stack switches to QUIC. Each QUIC packet carries a stream ID. Chromium's `quic::QuicConnection` processes UDP datagrams and ACKs each stream independently via selective ACKs, skipping blocked streams instead of waiting. The 0-RTT feature caches the server's encryption keys from the previous session, so on reconnection the client sends HTTP request data in the very first packet. Servers advertise HTTP/3 support via the `Alt-Svc` response header: ``` Alt-Svc: h3=":443"; ma=86400 ``` Nginx config that enables both: ```nginx listen 443 ssl; http2 on; http3 on; ssl_protocols TLSv1.3; add_header Alt-Svc 'h3=":443"; ma=86400'; ``` ### Common mistakes **Enabling HTTP/3 without opening UDP 443 on the firewall.** QUIC uses UDP port 443. Corporate firewalls often block all UDP. The result: the browser falls back to HTTP/2, no error is thrown, and you spend hours wondering why h3 is not showing in DevTools. ```bash # Open UDP 443 on Linux iptables -A INPUT -p udp --dport 443 -j ACCEPT # Verify QUIC is reachable curl --http3 https://yoursite.com # If you see: curl: (92) HTTP/3 not supported # UDP is blocked, or socket buffer is too small: sysctl net.core.rmem_max=2500000 ``` **Confusing HPACK and QPACK.** HTTP/2 uses HPACK with a shared dynamic table across all streams on a connection. HTTP/3 uses QPACK, which keeps per-stream tables to avoid HOL at the compression layer. Reusing an HTTP/2 HPACK setup on an h3 server causes header eviction bugs and can double header sizes. In nginx, set `http3_qpack_blocks 16384;` explicitly. **Assuming QUIC is always faster.** On stable high-bandwidth LANs, QUIC adds CPU overhead with no throughput benefit. Small-payload APIs on well-connected servers can actually regress. Benchmark before switching. **Ignoring 0-RTT replay risks.** QUIC 0-RTT resumes an encrypted session in the first packet, which is great for latency. But 0-RTT data can be replayed by a network attacker. Cloudflare restricts 0-RTT to GET requests only and skips POST/PUT. If you have state-changing GET endpoints, this matters. **Not testing h2 fallback in load tests.** Around 8% of users still lack HTTP/3 support. If your load test runs only on Chrome with h3, you miss the h2 traffic pattern, including TCP HOL spikes on congested paths. ### Real-world usage - Cloudflare routes 80% of its traffic over HTTP/3, including Discord bot backends. - YouTube uses h3 on mobile, reducing rebuffering events by around 10%. - Netflix ships video chunks via h3 and cut median start-play time by 25%, open-sourcing their QUIC implementation as `quic-go`. - Next.js 14+ detects the active protocol for streaming SSR responses. - Deno Deploy defaults to HTTP/3 for all edge deployments. ### Follow-up questions **Q:** What exactly is head-of-line blocking in HTTP/2 vs HTTP/3? **A:** In HTTP/2, a lost TCP packet blocks every stream on the connection until TCP retransmits it. In HTTP/3, QUIC handles loss per stream, so only the affected stream stalls. **Q:** How does QUIC's handshake beat TCP + TLS? **A:** TCP needs a 3-way handshake before TLS even starts. QUIC combines transport and crypto into one exchange: 1 RTT for new connections, 0-RTT for resumptions where keys are cached from the previous session. **Q:** What is QPACK and why does HTTP/3 need it instead of HPACK? **A:** HPACK uses a shared dynamic table across all streams. In HTTP/3, streams are independent, so a shared table would reintroduce HOL at the header layer. QPACK uses per-stream tables to avoid that. **Q:** What happens when a corporate firewall blocks UDP 443? **A:** The browser tries QUIC, fails, and falls back to HTTP/2 over TCP automatically. The user sees no error. You see no h3 traffic in logs. Check the `Alt-Svc` header and confirm UDP reachability with Wireshark using filter `quic`. **Q (senior):** On a network with 5% packet loss, quantify the throughput difference between HTTP/2 and HTTP/3. **A:** Based on Google's QUIC research, HTTP/2 drops around 40% throughput due to TCP tail-loss and retransmit timeouts. HTTP/3 with QUIC selective ACKs holds above 90%. The gap comes from TCP blocking the entire connection on a single loss event, versus QUIC's stream-local recovery. ## Examples ### Measuring the HOL blocking impact with parallel fetches The easiest way to see HOL blocking in action is Chrome DevTools with network throttling set to 3G. ```javascript // 10 parallel resource loads // HTTP/2: all stall if TCP drops a packet mid-transfer // HTTP/3: only the affected stream pauses const urls = Array.from({ length: 10 }, (_, i) => `/api/item/${i}`); const start = performance.now(); const results = await Promise.all( urls.map(url => fetch(url).then(r => r.json())) ); console.log(`All 10 loaded in ${(performance.now() - start).toFixed(0)}ms`); // On 1% packet loss (throttled in DevTools): // HTTP/2: ~4500ms (TCP HOL kicks in) // HTTP/3: ~2800ms (streams recover independently) ``` Watch the Protocol column in DevTools. Requests marked `h3` complete more consistently than `h2` under that condition. ### Connection migration in QUIC This is the feature that matters most for mobile apps but gets the least attention. ```javascript // HTTP/2 (TCP): when user switches WiFi to 4G, the IP changes. // TCP connection is tied to IP+port: new handshake, new TLS, ~500ms penalty. // HTTP/3 (QUIC): uses Connection ID, not IP+port. // When the network changes, the QUIC session continues with the same ID. // Connection ID: abc123 // WiFi: 192.168.1.5:443 → Connection: abc123 // 4G: 10.0.0.15:443 → Connection: abc123 (same session, no re-handshake) // For a video app: playback does not stutter // when the user walks from WiFi into cellular coverage. ``` I have seen this cause subtle bugs in session-tied auth tokens: the server sees the same Connection ID but a new IP, and some auth middleware flags it as a hijack attempt. Check what your middleware does with IP changes before enabling QUIC in production. ### Performance numbers by packet loss scenario ``` Packet loss 0%: HTTP/2: ~2500ms HTTP/3: ~2300ms (marginal gain) Packet loss 1%: HTTP/2: ~4500ms (TCP HOL starts hurting) HTTP/3: ~2800ms (QUIC recovers per stream) Packet loss 3% (typical 3G/4G congestion): HTTP/2: ~8500ms HTTP/3: ~3500ms (2.4x faster) ``` These numbers come from controlled load tests. Real results vary by server distance and CDN edge, but the pattern holds: the HTTP/3 advantage grows with packet loss and shrinks on clean connections.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.