HTTP/3's headline change isn't visible in any request trace you'll read — it's that streams now ride QUIC over UDP instead of TCP, which is what finally removes transport-level head-of-line blocking. HTTP/2 already multiplexes many streams over one connection, but because that connection is TCP, a single lost packet stalls every stream until the retransmit arrives — TCP guarantees in-order bytes, so it holds everything behind the gap. HTTP/3 maps each stream onto QUIC independently, so loss on one stream doesn't block the others. Your methods, headers, and status codes are identical across both.
| Property | HTTP/2 | HTTP/3 |
|---|---|---|
| Transport | TCP + TLS | QUIC over UDP |
| Stream multiplexing | Yes | Yes |
| Transport head-of-line blocking | Yes | Avoided across streams |
| Connection migration | Limited | Built into QUIC |
Where the difference actually shows up
The gain is real only on lossy links. On a clean wired connection with near-zero loss, HTTP/2 and HTTP/3 look nearly identical in a waterfall. Put the same page on a mobile network dropping ~1% of packets and the transport difference becomes the whole story:
| Moment | HTTP/2 over TCP | HTTP/3 over QUIC |
|---|---|---|
| Packet for asset #7 is lost | TCP holds all later bytes; streams 1–30 wait | Only stream #7 waits; 1–6 and 8–30 keep delivering |
| Retransmit lands ~1 RTT later | All 30 streams resume together | Stream #7 resumes; others already finished |
| User-visible effect | One drop stalls the whole page | One drop delays a single asset |
The takeaway: HTTP/3 doesn't make a healthy network faster — it stops an unhealthy one from converting a single lost packet into a whole-page stall. That's why the wins concentrate on mobile, transit, and long-haul international routes.
Common mistakes
- Flipping HTTP/3 on at the app while the CDN or load balancer still terminates at HTTP/2. The client negotiates with whatever answers first, so the app-level setting does nothing. HTTP/3 has to be enabled at the edge that faces the client.
- Assuming UDP is always reachable. Some corporate and captive networks block or throttle UDP, so clients must be able to fall back to HTTP/2 or HTTP/1.1 — which every browser does automatically, but only if you haven't broken the fallback path.
- Forgetting the
Alt-Svcheader. Browsers reach your origin over HTTP/2 first and only switch to h3 after they see anAlt-Svc: h3=...advertisement. No header, no upgrade — they stay on h2 indefinitely. - Treating the protocol as a latency fix. It can't shrink a 4 MB JSON body or speed up a slow database query. If those dominate your response time, the transport change is noise.
HTTP/2 already removed application-level request serialization for independent streams. HTTP/3 mainly removes the TCP-level loss coupling underneath them — a different layer, a different bottleneck, and neither one touches your backend latency.
Protocol upgrades are worthwhile performance work, but they aren't permission to stop measuring. Fix payload sizes and backend latency first, then enable HTTP/3 at the edge where it's a low-risk addition — and confirm the win on a genuinely lossy connection, because that's the only place the number moves.
Cover photo by Brett Sayles on Pexels.
