PATTERN Cited by 1 source
HTTP/3 over HTTP/2 for unreliable networks¶
Pattern¶
When given the choice between HTTP/2 (TCP) and HTTP/3 (QUIC/ UDP) for a transport-layer protocol, prefer HTTP/3 when the client-server path is both (a) unreliable — packet loss, mobile / cellular / transcontinental Wi-Fi — and (b) carries large payloads where TCP's head-of-line blocking compounds individual packet losses into whole-stream stalls. Use HTTP/2 as the default on reliable networks where QUIC implementation immaturity may cost more than TCP HoL blocking does.
Canonical instance¶
PlanetScale's 2023-01-04 benchmark (Source: sources/2026-04-21-planetscale-faster-mysql-with-http3):
- Small payloads (cold-start
SELECT 1): HTTP/2 and HTTP/3 statistically indistinguishable. No packet-loss pressure; both protocols amortise TLS 1.3 handshake cost the same way. - Medium payloads (~50 KB): HTTP/2 and HTTP/3 roughly equal; tail-latency improvements visible on high-latency links but not differentiating the two.
- Large payloads (~27.5 MB
SELECT; 2000-row bulkINSERT) on high-latency network: HTTP/2 shows minor improvements over MySQL; HTTP/3 pulls a big lead. Robenolt: "I suspect this is because, with this size of a payload, we're potentially able to exhibit packet loss and other warts of TCP, which QUIC smooths over." - Large payloads on low-latency network: HTTP/3
measurably slower than HTTP/2. Robenolt hypothesises
quic-golibrary immaturity: "my hypothesis is that this is a performance limitation in the underlyingquic-goimplementation due to it being a bit more immature and less battle-tested."
The operational conclusion: HTTP/3 wins only when the network's unreliability exceeds the QUIC library's implementation tax. On reliable low-latency networks the mature TCP + HTTP/2 stack still wins on large payloads.
Mechanism: why QUIC smooths over packet loss¶
TCP is a single ordered byte-stream at the transport layer. HTTP/2 multiplexes many streams on top of one TCP connection, but the multiplexing is application-layer only — at the transport layer, every byte must arrive in order. A single lost TCP segment blocks the entire connection until retransmit arrives, which means every HTTP/2 stream on that connection pauses — transport-level head-of-line blocking.
QUIC (the transport underlying HTTP/3) multiplexes streams at the transport layer. A lost packet carrying stream A's data does not block stream B; QUIC delivers stream B out-of-order to the application while retransmitting A. Only the affected stream stalls.
For a large payload split across many QUIC packets, this means a single packet loss event costs you the retransmit RTT for that packet — not a whole-connection stall. On a high-latency path with cumulative small loss events, the HTTP/2 disadvantage compounds multiplicatively.
When to use HTTP/3¶
- Mobile / cellular clients — packet loss is endemic on the radio access path.
- Long-distance WAN (transcontinental, intercontinental) — cumulative loss events add up over many router hops.
- Large payloads — the per-packet loss penalty is more likely to hit at least one packet; see sources/2026-04-21-planetscale-faster-mysql-with-http3's ~27.5 MB SELECT benchmark.
- Connection migration required — WiFi ↔ cellular handoff on mobile clients; QUIC's connection-ID-based identity (not 4-tuple) preserves state (concepts/quic-connection-migration).
When HTTP/2 still wins¶
- Low-latency LAN / same-region cloud — no packet loss in practice; QUIC library immaturity costs more than HoL blocking saves.
- Middlebox-hostile networks — some enterprise firewalls block UDP/443 or rate-limit it aggressively (concepts/udp-middlebox-hostility). HTTP/3 falls back to HTTP/2 in these contexts anyway; ship HTTP/2 as the first-line protocol and HTTP/3 as an opportunistic upgrade.
- Small payloads on warm connections — connection-cost amortisation dominates; protocol choice is noise.
Ecosystem caveats (as of 2023)¶
- Browser HTTP/3 support is solid: Chrome, Firefox,
Safari, Edge all ship HTTP/3 client and honour
Alt-Svcupgrade hints. - Non-browser HTTP/3 support is immature. Robenolt: "HTTP/3 lacks a lot of adoption outside of web browsers due to being radically different." Go's stdlib has no HTTP/3 client (PlanetScale's PoC uses lucas-clemente/quic-go experimentally); Node.js HTTP/3 support is behind flags; most ORMs and DB drivers do not speak HTTP/3 at all.
- Server-side HTTP/3 requires a QUIC implementation in the server process. nginx gained HTTP/3 support in 1.25 (2023); Caddy, Cloudflare, Google Cloud Load Balancer, and AWS CloudFront all support it at the edge; custom services need a user-space QUIC library and typically route HTTP/3 through a gateway rather than terminate it in-process.
Production deployment notes¶
PlanetScale's web console uses HTTP/3 transparently when a modern browser connects — no configuration toggle, no user- visible negotiation: "If you use the in-product PlanetScale web console and a modern browser, you will connect over HTTP/3 and not even be aware of it." The non-browser HTTP/3 deployment (the experimental Go driver from the benchmark post) remains a proof-of-concept; the production deployment pattern is: serve HTTP/3 from the edge for browsers, keep the server-side path TCP-friendly.
Related patterns¶
- patterns/http-api-alongside-binary-protocol — parent pattern. Shipping HTTP alongside the native binary protocol is the prerequisite; HTTP/3 vs HTTP/2 is a sub-choice within that.
- patterns/multiplex-many-database-connections-over-one-http-connection — sibling structural benefit that applies to both HTTP/2 and HTTP/3.
Seen in¶
- sources/2026-04-21-planetscale-faster-mysql-with-http3 — canonical quantified database-transport instance; the large-payload / high-latency quadrant where HTTP/3 beats HTTP/2 is where the pattern fires.