Skip to content

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 bulk INSERT) 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-go library immaturity: "my hypothesis is that this is a performance limitation in the underlying quic-go implementation 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-Svc upgrade 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.

Seen in

Last updated · 550 distilled / 1,221 read