HTTP/2, HTTP/3, QUIC Deep — Compute Reference

1. At a glance

This is the deep-dive companion to networking-foundations, which covers OSI / TCP / TLS / DNS / BGP at Tier-1 breadth. Here we focus narrowly on the modern HTTP stack: the binary HTTP/2 framing layer (2015), the QUIC transport (UDP-based, TLS 1.3 integrated, IETF-standardized 2021), and HTTP/3 (HTTP semantics rebound onto QUIC, RFC 9114 in 2022).

The evolution path is simple to state, harder to deploy:

  • HTTP/1.1 (RFC 2616 / 7230-7235, refreshed as 9110-9112 in 2022) — text-based, one request in flight per TCP connection, head-of-line blocked.
  • HTTP/2 (RFC 7540 in 2015 → consolidated RFC 9113 in 2022) — binary framing, multiplexed streams over a single TCP connection, HPACK header compression.
  • HTTP/3 (RFC 9114 in 2022) — same HTTP semantics, but transport is QUIC (RFC 9000) over UDP with TLS 1.3 baked into the handshake.

Adoption snapshot as of late 2025 (Cloudflare Radar, W3Techs measurement): roughly 30-40% of web requests across CDN-fronted traffic now negotiate HTTP/3. The remainder is mostly HTTP/2; HTTP/1.1 is residual but never going to zero (CI scripts, embedded clients, corporate proxies, server-to-server within VPCs).

The key design insight that drove the transition from HTTP/2 to HTTP/3 was the recognition that multiplexing N logical streams over a single TCP connection trades per-connection head-of-line blocking for per-packet head-of-line blocking: one dropped TCP segment now stalls every multiplexed stream until retransmission completes. QUIC solves this by moving the loss-recovery boundary into the transport itself and giving each stream independent reliability.

A note on terminology: when people say “QUIC” they usually mean IETF QUIC v1 (RFC 9000, May 2021), which is meaningfully different from “gQUIC” — Google’s pre-standard variant deployed internally from 2013 onward. gQUIC is essentially extinct in public traffic; Chrome and Cloudflare and Google’s own properties all moved to IETF QUIC during 2020-2022.

A second note on framing: “HTTP/3” and “QUIC” are routinely conflated, but they are two separate protocols. QUIC is a general-purpose secure transport; HTTP/3 is one application using it. DNS-over-QUIC (RFC 9250), SMB-over-QUIC (Microsoft), MASQUE (RFC 9298), WebTransport (W3C), and the emerging Media-over-QUIC (IETF MoQ WG) are all distinct application bindings to the same transport. Treating QUIC as a transport-layer peer of TCP — rather than as an HTTP-only thing — is the right mental model.

2. HTTP/1.1 limitations

The HTTP/1.1 spec is old (RFC 2068 in 1997, RFC 2616 in 1999, RFC 7230-7235 in 2014, RFC 9110-9112 in 2022 as the “HTTP semantics” refresh). The protocol works, but four limitations motivated the move to HTTP/2:

  1. Text-based parsing overhead. HTTP/1.1 frames are ASCII with CRLF separators and case-insensitive header names. Parsing requires careful state machines and is the source of many CVEs (request smuggling, header injection, etc.). Binary framing avoids whole classes of bugs.

  2. Head-of-line blocking on a single connection. Only one request can be in flight at a time (modulo pipelining, which never reliably worked because intermediate proxies misbehaved). Browsers compensated by opening up to 6 parallel TCP connections per origin — which costs RTTs of setup, fights TCP slow-start, and wastes server file descriptors.

  3. Pipelining is theoretically allowed (RFC 2616 § 8.1.2.2) but in practice was never reliably supported. Server responses must be returned in request order, and any non-idempotent request blocks others. Firefox enabled it briefly and reverted; Chrome never shipped it. Effectively dead by 2012.

  4. Inefficient header repetition. Every request carries the same Cookie, User-Agent, Accept, Accept-Encoding, Accept-Language, Referer headers — easily 800-2000 bytes per request. With 50-100 sub-resource requests for a typical page, that’s 40-200 KB of redundant headers. No compression of headers in HTTP/1.1.

A fifth, softer issue: TLS handshake cost. HTTP/1.1 over TLS 1.2 requires 2 RTTs of handshake before the first byte of HTTP can flow (TCP SYN/SYN-ACK/ACK, then TLS ClientHello/ServerHello/Finished). On a 100 ms RTT link that’s 200 ms of dead time before any payload moves. HTTP/2 inherits this; HTTP/3 collapses it via QUIC + TLS 1.3 integration.

3. HTTP/2 (RFC 7540, 2015 → RFC 9113, 2022)

HTTP/2 was standardized in May 2015 as RFC 7540, derived from Google’s SPDY protocol (2009-2015). It preserves HTTP/1.1 semantics (methods, headers, status codes) but completely changes the wire format. RFC 9113 (2022) consolidated errata, deprecated PRIORITY / PUSH_PROMISE, and split semantics out into RFC 9110.

3.1 Binary framing layer

All HTTP/2 communication is framed. Each frame is:

+-----------------------------------------------+
| Length (24)                                   |
+---------------+---------------+---------------+
| Type (8)      | Flags (8)     |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31)                                      |
+=+=============================================================+
| Frame Payload (0 to length-1)                                 |
+---------------------------------------------------------------+

Frame types (RFC 9113 § 6):

  • DATA (0x0) — request/response bodies.
  • HEADERS (0x1) — opens a stream and carries the header block.
  • PRIORITY (0x2) — deprecated in 9113; the priority tree (RFC 7540 § 5.3) was confusing and rarely implemented well. Superseded by RFC 9218 (HTTP priority hints) which uses simple urgency + incremental Structured Field values in a header.
  • RST_STREAM (0x3) — cancel a stream with an error code.
  • SETTINGS (0x4) — connection-level parameters; exchanged at connection start.
  • PUSH_PROMISE (0x5) — deprecated; server push proved to over-push and underperform browser cache prediction.
  • PING (0x6) — keepalive + RTT measurement.
  • GOAWAY (0x7) — initiate graceful shutdown.
  • WINDOW_UPDATE (0x8) — flow control credit.
  • CONTINUATION (0x9) — header block fragment continuation (when one block exceeds MAX_FRAME_SIZE).

3.2 Streams

A stream is a bidirectional sequence of frames sharing a stream identifier. Stream 0 is reserved for connection-level control (SETTINGS, PING, GOAWAY, WINDOW_UPDATE).

  • Client-initiated streams have odd IDs (1, 3, 5, …).
  • Server-initiated streams (only via PUSH_PROMISE) have even IDs (2, 4, …).
  • Stream IDs strictly increase within their parity class; reusing an ID is a protocol violation.
  • Maximum stream ID is 2^31 − 1; once exhausted, the connection must be closed (GOAWAY with the largest ID seen) and a new one started.

Stream lifecycle (RFC 9113 § 5.1): idle → reserved (local/remote) → open → half-closed (local/remote) → closed. Transitions happen on HEADERS, DATA with END_STREAM, RST_STREAM, or PUSH_PROMISE frames.

3.3 Multiplexing

The big win. A client can have N concurrent HTTP requests in flight on one TCP connection with no head-of-line blocking at the HTTP layer (TCP-layer HoL is a separate problem; see § 4). Each request gets its own stream; frames from different streams are interleaved on the wire. Browsers typically open one connection per origin (per SETTINGS_MAX_CONCURRENT_STREAMS, default 100, often higher in practice).

3.4 HPACK header compression (RFC 7541)

Headers are repetitive — HPACK exploits that. Three mechanisms:

  1. Static table — 61 hardcoded common headers (:method GET, :status 200, accept-encoding gzip, deflate, etc.). Reference by a single index byte.
  2. Dynamic table — sender-controlled FIFO of recently-sent name/value pairs. Default size 4 KB, negotiable via SETTINGS_HEADER_TABLE_SIZE. Each peer maintains its own dynamic table; sender mutates it via wire-format insertions.
  3. Huffman coding — a fixed Huffman code (specified in RFC 7541 Appendix B, derived from a corpus of HTTP traffic) is applied to literal string values.

Compression ratios in practice: 80-90% on a typical site’s repeated cookie + UA + accept headers. The CRIME-style attack on TLS-compressed headers does not apply to HPACK because HPACK compresses headers separately from any attacker-controlled body, but a related vulnerability (HPACK bombing — large header lists that explode the dynamic table) led to SETTINGS_MAX_HEADER_LIST_SIZE being enforced.

3.5 Flow control

Per-stream and per-connection. Each receiver advertises a window (default 65535 bytes, raised via SETTINGS_INITIAL_WINDOW_SIZE) and replenishes it via WINDOW_UPDATE frames. This is separate from TCP’s window — it lets the application slow a slow consumer (e.g., a download to a phone with a backed-up disk) without affecting other streams on the same connection.

The default 64 KB initial window is a famous performance footgun on long-fat-pipe (high BDP) links. Most production servers set SETTINGS_INITIAL_WINDOW_SIZE to 6 MB or 16 MB.

3.6 Server push (deprecated)

PUSH_PROMISE lets the server preemptively send resources it expects the client to request. In theory: server sees a request for /index.html, pushes /style.css and /app.js before the client parses the HTML. In practice: servers over-push (sending things the client already has cached), browsers added complex cancellation logic, and net measurements (Chrome team 2020) found server push hurt page load in most cases.

Chrome removed support in 2022 (M106). Firefox followed. Server push is deprecated in RFC 9113; replacements are:

  • 103 Early Hints (RFC 8297) — the server returns a 103 response with Link: rel=preload headers while the real 200 is still being generated, letting the browser start fetching critical sub-resources during server compute time.
  • Speculation Rules API (W3C, Chrome 109+) — declarative client-side prefetch hints in the HTML.

3.7 SETTINGS frame

Connection-scoped parameters exchanged immediately after the connection preface. Notable ones:

  • SETTINGS_HEADER_TABLE_SIZE — HPACK dynamic table cap (default 4096).
  • SETTINGS_ENABLE_PUSH — 0 disables PUSH_PROMISE (now usually 0).
  • SETTINGS_MAX_CONCURRENT_STREAMS — soft cap on in-flight streams (typically 100-256).
  • SETTINGS_INITIAL_WINDOW_SIZE — per-stream flow control window (default 65535, raise this).
  • SETTINGS_MAX_FRAME_SIZE — payload size cap (16384 default to 16 MB max).
  • SETTINGS_MAX_HEADER_LIST_SIZE — defense against HPACK bombs.

A subtle issue: SETTINGS_MAX_CONCURRENT_STREAMS is advisory — the spec says the peer should not open more streams than this, but the cap is enforced by the receiver via RST_STREAM(REFUSED_STREAM) on overflow. In practice, browsers and clients are well-behaved, but a malicious peer can attempt to open more. The 2023 “HTTP/2 Rapid Reset” CVE (CVE-2023-44487, exploited by a ~398 Mrps DDoS observed by Google/Cloudflare/AWS) exploited the cheap stream cancellation primitive — an attacker would open a stream then immediately RST_STREAM, looping at high rate to bypass concurrency caps. Mitigations: rate-limit stream cancellation, count canceled streams against the concurrency cap until ack, deprioritize misbehaving connections. All major HTTP/2 stacks shipped patches in October 2023.

4. The TCP-level HoL blocking problem

HTTP/2 multiplexes streams logically but rides on TCP, which delivers bytes in order. If a single TCP segment carrying bytes for stream 3 is dropped, the TCP receive buffer holds back all subsequent segments — including ones carrying frames for streams 5, 7, 9, etc. — until the retransmission of the lost segment arrives.

This is the head-of-line problem moved down a layer. On a clean wired network with sub-1% loss, it barely matters. On a 4G mobile link with 2-5% loss, or a satellite link with bursty loss, it can negate most of HTTP/2’s multiplexing benefit. Measurements from Akamai and Cloudflare in 2018-2020 consistently showed HTTP/2 underperforming HTTP/1.1 on high-loss links because of this.

Fixing this requires moving multiplexing below the in-order delivery boundary — which is the entire reason QUIC exists.

5. QUIC (RFC 9000-9002, 2021)

QUIC began at Google in 2013 (Jim Roskind, then Ian Swett’s team) and ran in production on Google services from 2014. IETF picked it up in 2016; standardization completed May 2021 across:

  • RFC 9000 — QUIC v1 transport.
  • RFC 9001 — Using TLS 1.3 to secure QUIC.
  • RFC 9002 — Loss detection and congestion control.

QUIC is a transport protocol layered on UDP. It is not a tweak of TCP — it’s a fresh design that treats encryption as a first-class concern.

5.1 Handshake — 1-RTT and 0-RTT

TCP + TLS 1.2 needs 3 RTTs before payload (1 for TCP, 2 for TLS 1.2 full handshake). TCP + TLS 1.3 needs 2 RTTs. QUIC needs 1 RTT on first contact (the TLS 1.3 ClientHello rides in the very first UDP datagram alongside QUIC’s transport parameters), and 0 RTT on subsequent connections to the same server via session resumption — payload bytes can be in the first datagram from the client.

The cost: 0-RTT data is replayable by an attacker who captured the original packet (the server can’t yet prove freshness). Applications must restrict 0-RTT to idempotent operations — for HTTP, that means GETs only, no POSTs, no state-mutating actions. Servers typically implement an anti-replay cache with bounded window (Cloudflare publishes their design).

5.2 Stream-level multiplexing with independent recovery

QUIC has its own stream concept (RFC 9000 § 2). Streams are independent: a packet loss carrying stream 4’s bytes does not delay delivery of stream 8’s bytes that arrive after. Each stream tracks its own offset; the QUIC layer reassembles per-stream sequences from out-of-order packets.

Each QUIC stream maintains its own offset, FIN, and RESET_STREAM state. A stream is uniquely identified by its 62-bit ID, and the data it carries is treated as a byte sequence (like TCP) — application boundaries are not preserved within a stream. If you want message framing, the application protocol (HTTP/3, MoQ, your custom protocol) must impose it.

Stream IDs use a 62-bit unsigned integer. Two low bits encode initiator (client/server) and direction (bidi/uni), so there are four stream-ID spaces:

  • 0x0 — client-initiated bidirectional.
  • 0x1 — server-initiated bidirectional.
  • 0x2 — client-initiated unidirectional.
  • 0x3 — server-initiated unidirectional.

5.3 Integrated encryption

Almost the entire QUIC packet is encrypted. The visible plaintext is:

  • A short public header byte.
  • The destination connection ID (variable length, 0-20 bytes, chosen by the receiver).
  • For long-header packets only: source connection ID, version, length fields.

Packet numbers, frame types, payload — all encrypted with AEAD (TLS 1.3 keys: AES-128-GCM, AES-256-GCM, or ChaCha20-Poly1305). Even the packet number is protected by header protection, a separate XOR mask derived from the AEAD key, so on-path observers can’t easily track sequence numbers for traffic analysis.

This breaks every existing middlebox that did deep packet inspection of TCP+TLS. It’s a deliberate design choice — middlebox ossification of TCP was a major lesson from 30 years of TCP evolution attempts.

5.4 Connection migration

A QUIC connection is identified by its connection ID, not by the IP:port 4-tuple. If a client’s IP changes (Wi-Fi to LTE, NAT rebinding, multi-homing), packets carrying the same connection ID land on the same connection state at the server. The server validates the new path with PATH_CHALLENGE / PATH_RESPONSE frames to prevent amplification, then continues seamlessly.

This is huge for mobile. A streaming video that previously had to tear down and re-establish on network switch now continues; an in-flight upload survives.

5.5 Connection ID rotation for privacy

Both endpoints can issue replacement connection IDs via NEW_CONNECTION_ID frames. The sender then rotates which ID it uses, preventing on-path observers from linking a single connection across multiple network paths or NAT rebindings. This is privacy gold for anti-tracking work (Apple iCloud Private Relay uses it heavily).

5.6 Datagrams (RFC 9221, 2022)

QUIC’s DATAGRAM frame carries unreliable, unordered application data. No retransmission, no ordering, no flow control beyond MTU. Use cases:

  • Real-time media (video conferencing, gaming) that benefits from low latency over delivery guarantees.
  • DNS over QUIC for short queries.
  • MASQUE / CONNECT-UDP tunneling of UDP packets.
  • WebTransport datagram channel.

Datagrams share the same encryption + connection migration benefits as streams.

5.7 Pluggable congestion control

QUIC defers congestion control to the implementation. RFC 9002 specifies NewReno as a baseline + framework for others. In practice deployed:

  • CUBIC (Ha et al. 2008) — Linux default for TCP; widely used for QUIC too.
  • BBR v1, v2, v3 (Cardwell et al. Google 2016+) — models the bottleneck bandwidth and propagation RTT explicitly; substantially better on lossy and bufferbloated links. BBRv3 (2023+) is the production default in Google’s QUIC stack and Cloudflare’s quiche.
  • Vegas — RTT-based, rare in production but used in research.

Switching congestion controllers is trivial in userspace QUIC — change a setting — vs the kernel module dance for TCP.

5.8 Modern loss recovery

RFC 9002 specifies RACK-TLP (Recent ACKnowledgment + Tail Loss Probe) and a time-based detection scheme, not the old TCP duplicate-ACK trigger. Loss is detected when either (a) a packet sent some kPacketThreshold packets before an acknowledged packet remains unacknowledged, or (b) kTimeThreshold time has elapsed. This is faster + more accurate than NewReno’s fast-retransmit.

6. HTTP/3 (RFC 9114, 2022)

HTTP/3 is the HTTP semantics layer (same as HTTP/1.1 and /2 from a developer’s perspective: methods, headers, status codes from RFC 9110) bound to QUIC streams.

6.1 Frame mapping

Each HTTP request-response pair uses one client-initiated bidirectional QUIC stream. Frame types are simpler than HTTP/2 because QUIC handles multiplexing, prioritization, and flow control at the transport layer:

  • DATA (0x0) — request/response body.
  • HEADERS (0x1) — header block (QPACK-encoded).
  • CANCEL_PUSH, SETTINGS, PUSH_PROMISE, GOAWAY, MAX_PUSH_ID — control frames on a dedicated control stream.

No WINDOW_UPDATE — flow control is QUIC’s job. No PING — QUIC PINGs. No RST_STREAM — QUIC’s STOP_SENDING + RESET_STREAM.

6.2 QPACK (RFC 9204)

QPACK is HPACK redesigned to tolerate out-of-order stream delivery. The problem: HPACK’s dynamic table mutates as headers arrive, requiring strict ordering. But QUIC streams arrive out of order. Naive HPACK over QUIC would force serialization, killing the benefit.

QPACK splits the dynamic table into:

  • An encoder stream (unidirectional, ordered) that inserts new entries into the table.
  • A decoder stream that ACKs insertions back to the encoder.
  • Header blocks reference entries by absolute index, with a “Required Insert Count” that tells the decoder which table state is needed.

If a header block arrives before its required inserts, the decoder blocks only that stream until the inserts arrive. Other streams progress independently. Implementations tune how aggressively to use the dynamic table vs sending literals (the safe-but-suboptimal mode is “never block streams” — pay slightly worse compression for guaranteed liveness).

6.3 Server push

Defined in RFC 9114 (analogous to HTTP/2’s PUSH_PROMISE) but rarely used and increasingly considered a misfeature; Chrome has signaled it may not implement HTTP/3 server push at all.

6.4 Alt-Svc and discovery

HTTP/3 has no well-known port and no version negotiation at TCP — clients first connect HTTP/1.1 or /2 over TCP+TLS, then the server advertises HTTP/3 availability via the Alt-Svc header:

Alt-Svc: h3=":443"; ma=86400

On subsequent connections to the same origin, the client tries HTTP/3 first. The DNS HTTPS RR (RFC 9460, 2023) lets clients learn this on the first request without a TCP round-trip. Cloudflare and Google publish HTTPS RRs widely.

The Alt-Svc cache is per-origin and time-bounded (ma= parameter, max-age in seconds). When it expires, the client falls back to whatever HTTP version it last successfully negotiated. Browsers also implement a “broken HTTP/3 detector” — if the first HTTP/3 attempt fails after Alt-Svc advertisement, they suppress HTTP/3 for that origin for a back-off interval (Chrome starts at ~5 minutes, doubling on repeated failure). This protects against transiently flaky middlebox paths.

7. Performance impact

Real-world measurements across major properties (Google, Cloudflare, Facebook, Akamai 2020-2024) consistently show:

  • 0-RTT QUIC saves 100-300 ms on cold connections relative to TCP+TLS 1.2. Smaller savings vs TCP+TLS 1.3 (~50-150 ms).
  • Stream-independent loss recovery removes TCP HoL on lossy networks. Mobile networks (LTE, 5G NSA) see 5-20% reduction in median TTFB.
  • Connection migration dramatically reduces re-buffering events for streaming video on network handoffs.
  • CPU cost — userspace QUIC stacks initially were 2-3x the CPU of kernel TCP+TLS for the same throughput because the kernel offload path didn’t exist. The gap has closed substantially with:
    • io_uring (Linux 5.1+, mature in 5.11+) for batched I/O.
    • IORING_OP_SEND_ZC (Linux 6.0+) for zero-copy UDP send.
    • SO_REUSEPORT + SO_ATTACH_REUSEPORT_CBPF for per-CPU connection sharding by connection ID.
    • GSO/GRO for UDP (UDP_SEGMENT, generic receive offload) — bundles multiple small datagrams into one syscall.

Cloudflare’s blog posts (2022-2024) document driving HTTP/3 CPU within ~15-25% of HTTP/2’s at line rate. Microsoft’s msquic on Windows uses RIO (Registered IO) for similar gains.

Beyond bare CPU, memory and connection state matter: a QUIC connection carries more in-process state than a TCP connection (per-stream offsets, AEAD keys, packet number spaces, recovery state, congestion controller state). For a server fronting 1M concurrent connections, the per-connection state delta is on the order of 5-15 KB; the connection ID + IP + port lookup tables and recovery timers also add overhead. msquic, quiche, and mvfst all document techniques for memory pooling, fixed-size per-connection buffers, and lazy state allocation to keep this manageable.

8. Major implementations

QUIC is implementation-heavy. Notable production stacks:

  • Cloudflare quiche (Rust) — CDN edge, also exposed as a Go binding (quiche-go) and the basis of cloudflared. Boring TLS backend.
  • NGINX — native QUIC and HTTP/3 stable since 1.25.0 (May 2023). Earlier patches existed via the nginx-quic fork.
  • Caddy — HTTP/3 out of the box since v2.6 (Sept 2022); uses quic-go under the hood.
  • Microsoft msquic (C) — used in IIS 10+, .NET Kestrel (Kestrel HTTP/3 GA in .NET 7, Nov 2022), Windows 11’s SMB-over-QUIC, and exposed via WinHTTP for clients.
  • Apple Network.framework + URLSession — HTTP/3 client support since iOS 15 / macOS Monterey (2021). Used by Safari, App Store, iCloud.
  • quic-go (Lucas Clemente, Go) — production-quality, used by Caddy, Cloudflare’s go bindings, syncthing, etc.
  • Facebook mvfst (C++) — used in Facebook + Instagram traffic at scale; permissive license.
  • aioquic (Python, Jeremy Lainé) — pure-Python QUIC + HTTP/3, used by Python research tools and by the IETF reference implementation lists.
  • picoquic (C, Christian Huitema) — research-grade, used heavily in interop testing; intentionally small.
  • Mozilla neqo (Rust) — Firefox’s QUIC stack; pairs with NSS for TLS.
  • Chromium’s net/quic stack — built-in, drives Chrome / Edge / every Chromium fork.

There are also more specialized stacks: F5’s BIG-IP QUIC, HAProxy QUIC (added 2.6, June 2022, much improved in 2.7+), LiteSpeed’s LSQUIC (used in OpenLiteSpeed + LiteSpeed Web Server).

9. Browser support

All current major browsers default-on for HTTP/3:

  • Chrome / Edge (Chromium) — enabled by default since Chrome 87 (Nov 2020) when the server advertises Alt-Svc.
  • Firefox — default-on since Firefox 88 (April 2021).
  • Safari — supports HTTP/3 since Safari 14 (macOS Big Sur, Nov 2020); enabled by default on iOS 14 / macOS 11.

Curl supports HTTP/3 via --http3 since 7.66 (with nghttp3 + ngtcp2, or with quiche). It still requires a build flag (./configure --with-nghttp3 --with-ngtcp2 --with-openssl-quic or similar) but most distro packages now ship with it.

10. Server adoption

CDN-side adoption is mature:

  • Cloudflare — HTTP/3 GA in 2019, every customer by default.
  • Fastly — HTTP/3 GA in 2021.
  • Google Cloud (Cloud CDN, Cloud Load Balancing) — HTTP/3 GA in 2022.
  • AWS CloudFront — HTTP/3 GA August 2022; one-click enable per distribution.
  • Akamai — HTTP/3 GA 2022, broad rollout 2023.
  • Microsoft Azure Front Door — HTTP/3 GA 2023.

Origin-server adoption is more mixed:

  • nginx 1.25.0+ (May 2023) — supports listen 443 quic and HTTP/3.
  • Apache HTTPDmod_http3 is experimental as of 2026 and not in mainline; most production Apache deployments still terminate HTTP/1.1 / /2 only.
  • Caddy — default-on since v2.6.
  • HAProxy — solid QUIC since 2.6 (June 2022).
  • Traefik — experimental HTTP/3 since v2.9 (2022), opt-in.
  • Envoy — HTTP/3 GA in 2022; used by Istio + many service mesh deployments.

Cloudflare Radar shows ~30-40% of all HTTPS requests transiting their edge are HTTP/3 by mid-2025, climbing roughly 1-2 percentage points per quarter.

11. Other QUIC applications beyond HTTP/3

QUIC isn’t just for HTTP. Several other application protocols ride on it:

  • DNS over QUIC (DoQ) — RFC 9250 (May 2022). Standard port UDP/853. Avoids TCP setup latency for DNS-over-encrypted; AdGuard, Cloudflare’s 1.1.1.1, NextDNS support it.
  • SMB over QUIC — Microsoft, Windows Server 2022+ / Windows 11 Enterprise. Lets file shares traverse internet without VPN; mobile workers connect to corporate file shares over a QUIC tunnel on UDP/443.
  • MASQUE / CONNECT-UDP — RFC 9298 (Aug 2022). Tunnels UDP packets inside QUIC streams, enabling VPN-like proxying that traverses any network allowing UDP/443. Apple iCloud Private Relay uses MASQUE for its two-hop architecture. Cloudflare published a MASQUE implementation in 2022.
  • CONNECT-IP — RFC 9484 (Oct 2023). Extends MASQUE to tunnel arbitrary IP packets; closer to a full VPN replacement.
  • WebTransport (W3C) — browser-side bidirectional streams + unreliable datagrams over HTTP/3; partial WebSocket + WebRTC DataChannel replacement.
  • QUIC datagrams for real-time media — emerging Media-over-QUIC (MoQ) IETF WG (chartered 2022) explores low-latency live video over QUIC datagrams, potentially replacing SRT + parts of WebRTC.

12. Congestion control + recovery

Worth a dedicated section because QUIC made experimentation easy. The main algorithms in production:

  • NewReno (RFC 6582, baseline) — the RFC 9002 default. Loss-based; halves congestion window on loss. Conservative.
  • CUBIC (Ha, Rhee, Xu 2008) — Linux TCP default since 2.6.19; works for QUIC. Cubic function of time since last loss for cwnd growth; aggressive on long-fat-pipe links.
  • BBR v1 (Cardwell et al. Google 2016) — explicitly models BtlBw (bottleneck bandwidth) and RTprop (round-trip propagation time). Loss-tolerant. Initially unfair to CUBIC flows in some scenarios.
  • BBR v2 (Google 2019+) — adds explicit loss + ECN response, fairer to loss-based flows.
  • BBR v3 (Google 2023+) — further refinements; production default in Google’s QUIC stack and Cloudflare’s quiche by 2024.
  • HyStart++ (RFC 9406, 2023) — better detection of slow-start exit point; reduces overshoot.
  • Pacing — sender spreads packet emissions over RTT rather than sending in cwnd-sized bursts. Reduces queueing + loss. QUIC implementations all pace by default; TCP pacing is opt-in (tcp_pacing_ca_ratio sysctl).
  • ECN (Explicit Congestion Notification) — IP-layer marking (ECN bits in IP header) instead of drop. RFC 9002 specifies ECN handling for QUIC. Deployment historically blocked by middleboxes “bleaching” the ECN bits to zero; QUIC’s path validation can detect bleaching.
  • L4S (Low Latency, Low Loss, Scalable; RFC 9330-9332, 2023) — uses ECN signal with much higher fidelity; emerging on Comcast and some ISPs.
  • Loss detection — RACK-TLP (RFC 8985, 2021) is the production standard for both TCP and QUIC; replaces dupack-based fast-retransmit.
  • PRR (Proportional Rate Reduction) — RFC 6937; smooth window reduction during fast recovery without the abrupt halving of classic NewReno. Used by Linux TCP and by most QUIC stacks.
  • Initial window — QUIC’s recommended initial cwnd is 10 MSS (matching RFC 6928 for TCP), but stacks experiment. Cloudflare has shipped larger initial windows for low-RTT paths and seen meaningful improvement on small-object workloads.

A subtle point about congestion control in QUIC: because each QUIC implementation owns its CC algorithm, you can mismatch client and server. A client running BBRv3 talking to a server running CUBIC is fine — congestion control is one-sided (each endpoint manages its own sending rate). This is freeing compared to TCP, where CC choice was historically a kernel-wide setting per direction.

13. Flow control

QUIC has three layers of flow control (RFC 9000 § 4):

  • Connection-levelMAX_DATA frame advertises total bytes the receiver is willing to accept across all streams.
  • Stream-levelMAX_STREAM_DATA per stream; sender can’t exceed this offset on the stream.
  • Stream-count-levelMAX_STREAMS caps how many streams of a given type the peer can open.

Flow control is separate from congestion control. Congestion control says “how fast may I send into the network”; flow control says “how much has the receiver promised to buffer.” Both must allow a send for it to proceed.

Production tuning: initial window values that are too small (defaults are conservative — often 64 KB stream / 16 MB connection) bottleneck throughput on long-fat-pipe links. Bump initial_max_data and initial_max_stream_data_* transport parameters to the multi-MB range for high-BDP paths.

14. Encryption + privacy

QUIC integrates TLS 1.3 (RFC 8446, see cryptography-fundamentals) directly into the handshake. There is no “TLS over QUIC” — TLS 1.3 messages ride inside QUIC CRYPTO frames in the very first datagrams, and the AEAD keys derived from TLS protect QUIC packets immediately.

Beyond the basic handshake, several privacy primitives matter:

  • ECH (Encrypted Client Hello) — IETF draft, deployed in Cloudflare + Mozilla 2023+. Encrypts the SNI (the server name in the TLS ClientHello), preventing on-path observers from learning which site a client is contacting. Replaces the failed ESNI draft. Requires DNS HTTPS RR to publish the ECH public key; Chrome, Firefox, Safari are progressively enabling it.
  • ECN bleaching mitigation — QUIC validates ECN on the connection path and falls back gracefully if middleboxes strip the bits.
  • Metadata minimization — most of what would be plaintext in TCP+TLS (sequence numbers, ack ranges, stream control) is encrypted. By contrast, HTTP/2 over an unencrypted connection (h2c, rare in practice) exposes headers in plaintext.
  • Connection ID rotation — covered in § 5.5; prevents on-path linkability across NAT rebindings.
  • Padding — QUIC’s PADDING frame can pad packets to fixed sizes for traffic analysis resistance, used heavily by Tor-over-QUIC research and Apple Private Relay.

The privacy story is the strongest argument for QUIC outside of pure performance.

15. gRPC over HTTP/2 vs HTTP/3

gRPC (Google’s RPC framework, open-sourced 2015) is currently locked to HTTP/2 — the spec defines the frame mapping in terms of HTTP/2 HEADERS + DATA frames, trailers, and stream cancellation semantics. A draft “gRPC over HTTP/3” spec emerged in 2024 with experimental implementations in grpc-go (and pre-existing community work in grpc-rust and grpc-dotnet).

Why does this matter? Mobile and edge. gRPC’s strength has been efficient binary multiplexed RPC, but the TCP HoL blocking penalty hurts gRPC just as much as plain HTTP traffic on lossy networks. HTTP/3 fixes that. Expected wide adoption window: 2026-2028.

Note that gRPC-Web (the browser-friendly variant) is already incidentally HTTP/3-compatible since it rides on plain HTTP/{1,2,3}.

16. WebTransport (W3C)

WebTransport is a browser JS API that exposes HTTP/3 streams + datagrams to web apps. The spec went to W3C Candidate Recommendation in 2024 and ships in Chrome / Edge (stable), Firefox (behind a flag as of early 2026), Safari (in TP).

The API:

const wt = new WebTransport("https://example.com/wt");
await wt.ready;
// Bidirectional stream
const stream = await wt.createBidirectionalStream();
const writer = stream.writable.getWriter();
await writer.write(new Uint8Array([1, 2, 3]));
// Unreliable datagram
const dgWriter = wt.datagrams.writable.getWriter();
await dgWriter.write(new Uint8Array([0xff]));

Use cases it cleanly addresses:

  • Game networking (datagrams + multiple ordered streams) — what WebSocket was bad at.
  • Low-latency live video / voice control planes.
  • Cloud gaming control channels.
  • Real-time collaborative editing.

Fallback story: there’s a “WebTransport over HTTP/2” spec for when QUIC is blocked, but it has weaker properties (no datagrams, no migration). In practice, deployments use it sparingly as a fallback.

17. HTTP/3 over WebSockets fallback

Roughly 5-10% of corporate networks block UDP egress to arbitrary destinations. Some carriers throttle UDP. Some hotel / coffee shop networks block UDP/443 specifically. Some symmetric NATs make QUIC unusable.

Production deployments must have a TCP fallback. The Alt-Svc model handles this naturally: client tries HTTP/3, fails, falls back to whatever the underlying HTTP/1.1 or /2 connection was. Libraries (curl, browsers, msquic, quiche) implement happy-eyeballs-style racing — open QUIC and TCP+TLS in parallel, use whichever completes first, suppress the other.

Application-level protocols like gRPC, WebTransport, and MoQ also tend to specify both QUIC and TCP fallbacks.

18. Diagnostics + observability

QUIC’s encryption breaks tcpdump-style debugging. Tooling has evolved:

  • qlog — a structured JSON event log format proposed by Robin Marx (Hasselt University) in 2020. Most QUIC implementations emit qlog (or can be configured to). It captures packets sent / received, ACKs, frame contents, congestion state, RTT estimates, etc.
  • qvis — Marx’s web-based visualizer that ingests qlog and renders sequence diagrams, congestion graphs, packet loss timelines. Indispensable for diagnosing QUIC perf issues.
  • Wireshark — has a QUIC dissector since 3.0, with HTTP/3 + QPACK since 3.4. Requires the TLS secret keys (export via SSLKEYLOGFILE env var, supported in Chrome, Firefox, curl, msquic) to decrypt and dissect frame contents.
  • Cloudflare Radar — public dashboard of HTTP version share, TLS versions, IPv6 share, etc.
  • RIPE Atlas — measurement probes for routing + reachability across QUIC.
  • curl --http3 — minimal repro / smoke test. Add -v --trace for verbose output. Combined with SSLKEYLOGFILE + Wireshark, full visibility.
  • Chromium net-internals (chrome://net-internals/#quic) — live view of QUIC sessions, sessions cached, ALPN, server config.

Tracing libraries: OpenTelemetry has HTTP semantic conventions covering HTTP/3 just like /2; spans should record http.flavor=3.0 for clarity.

19. Production lessons

Hard-won wisdom from large operators:

  • UDP is blocked or throttled by some corporate firewalls (often via group policy on Windows endpoints) and a handful of carrier networks. Always offer Alt-Svc but never require HTTP/3. Browsers fall back automatically; bespoke clients must too.
  • DDoS amplification — UDP is historically the source of reflection-amplification attacks. QUIC v1 mitigates with an amplification factor cap: a server may send at most 3× the bytes it has received from an unverified peer (RFC 9000 § 8.1). This caps the amplification factor strictly. The cost: the very first server response is small; full bidirectional capability waits for path validation.
  • Anycast load balancing — when multiple servers behind a single anycast IP terminate QUIC, a routing change can dump packets for an in-progress connection on a server that doesn’t have the connection state. Cloudflare’s solution: encode the routing target inside the connection ID itself (using consistent hashing on the CID), so any frontend in the anycast set knows which backend owns the connection from the CID alone. This is now standard architecture for large-scale QUIC.
  • 0-RTT replay risk — QUIC 0-RTT data can be replayed by a network attacker who captured the original packet. Restrict 0-RTT to idempotent operations (HTTP GETs, no POST/PUT/DELETE). Most QUIC stacks expose a knob; the HTTP/3 layer in Chromium, msquic, and quiche all default 0-RTT to GETs only.
  • CPU cost — userspace QUIC + TLS does substantially more work per byte than kernel TCP + kernel TLS offload. The gap has closed (io_uring, UDP GSO/GRO, SO_REUSEPORT + connection-ID sharding) but on older hardware (pre-Cascade Lake / pre-Zen 2) HTTP/3 can still cost ~30% more CPU than HTTP/2 at line rate. Plan capacity accordingly.
  • MTU + path discovery — QUIC must avoid IP fragmentation (which defeats stateless retry validation and hurts performance). RFC 8899 (DPLPMTUD) defines a Datagram PLPMTUD for QUIC; implementations probe path MTU dynamically. Default initial datagram is conservative (1200 bytes).
  • NAT rebinding intervals — many home NATs drop UDP state aggressively (30-60 s). QUIC connections going idle need PING frames or app-level keepalives more frequently than TCP connections to survive NAT timeouts.
  • Spin bit — QUIC’s spin bit (RFC 9000 § 17.4) is a single bit that toggles per RTT, exposed to on-path observers for passive RTT measurement. Optional; some implementations disable it for paranoid privacy.
  • GOAWAY semantics differ — HTTP/3 GOAWAY signals the largest stream ID the peer will process; outstanding higher-ID streams must be retried on a new connection. Important for graceful shutdown during deploys; load balancers must implement connection draining differently for QUIC than for HTTP/2.
  • Idle timeout — QUIC requires negotiating an idle timeout (no traffic for N ms → connection silently dies). Default values are short (30 s common). Long-lived API connections need either app keepalives or longer negotiated timeouts.
  • Observability gaps — many existing APM / metrics pipelines were built around TCP socket stats (/proc/net/tcp, eBPF probes on tcp_* kernel functions). Equivalent visibility for userspace QUIC requires either qlog (high overhead, sample it) or in-process exporters. Plan instrumentation work as part of any HTTP/3 rollout.
  • Buffer bloat interaction — QUIC pacing + BBR works well against bufferbloated paths, but if you control both endpoints, also consider CAKE / fq_codel at the bottleneck. Big asymmetric residential links often have multi-second buffers that pacing alone can’t fully neutralize.
  • eBPF and XDP — line-rate QUIC servers increasingly use XDP to do UDP packet steering (connection-ID hash → CPU queue → userspace ring) without taking a sock lock. Cloudflare and Meta papers describe this. Required reading if you’re building a QUIC LB.

20. Standards landscape (2026)

The IETF QUIC ecosystem in 2026:

  • QUIC v1 — RFC 9000 (transport), RFC 9001 (TLS), RFC 9002 (recovery). Production standard.
  • QUIC v2 — RFC 9369 (May 2023). Mostly version-negotiation hardening; differs from v1 mainly in packet types + Salt. Encourages exercising version negotiation paths. Production deployment slowly rolling out.
  • HTTP/3 — RFC 9114 (June 2022).
  • QPACK — RFC 9204 (June 2022).
  • HTTP semantics — RFC 9110 (June 2022; same semantics layer used by HTTP/1.1, /2, /3).
  • HTTP/1.1 wire format — RFC 9112 (June 2022).
  • HTTP/2 wire format — RFC 9113 (June 2022; deprecates PRIORITY + PUSH_PROMISE).
  • HTTP priority hints — RFC 9218 (June 2022; replaces HTTP/2 priority tree).
  • DoQ — RFC 9250 (May 2022).
  • QUIC datagrams — RFC 9221 (March 2022).
  • MASQUE / CONNECT-UDP — RFC 9298 (August 2022).
  • CONNECT-IP — RFC 9484 (October 2023).
  • DNS HTTPS / SVCB RRs — RFC 9460 (November 2023). Lets DNS publish ALPN + ECH config + IP hints.
  • HyStart++ — RFC 9406 (May 2023).
  • L4S — RFCs 9330-9332 (January 2023).
  • Multipath QUIC — IETF draft (draft-ietf-quic-multipath) ongoing through 2024-2026; allows a QUIC connection to span multiple network paths simultaneously (Wi-Fi + LTE concurrently). Apple has shipped a related proprietary multipath extension in iOS for years; standardization aligning it.
  • QUIC ACK frequency — RFC 9618 (October 2024); reduces ACK overhead.
  • Media over QUIC (MoQ) — WG chartered 2022, drafts active. Low-latency media transport using QUIC streams + datagrams. Possibly the biggest emerging app for QUIC after HTTP/3 itself.

The standards velocity is high — expect multipath QUIC, MoQ, and ECH to all converge to RFCs by 2027.

21. Cross-references

  • networking-foundations — the Tier-1 networking foundations note; covers OSI, TCP, UDP, TLS, DNS, BGP at breadth.
  • _index — the Compute MOC.
  • cryptography-fundamentals — TLS 1.3, ECH, AEAD, key derivation; QUIC’s encryption layer relies on this.
  • observability-stack — qlog, distributed tracing, metrics; QUIC’s observability story sits inside the broader stack.
  • connector-families — physical-layer connector context (RJ45 / SFP / QSFP / fiber) underneath the wire we’re discussing here.

22. Citations

  • RFC 9000QUIC: A UDP-Based Multiplexed and Secure Transport, May 2021.
  • RFC 9001Using TLS to Secure QUIC, May 2021.
  • RFC 9002QUIC Loss Detection and Congestion Control, May 2021.
  • RFC 9114HTTP/3, June 2022.
  • RFC 9204QPACK: Field Compression for HTTP/3, June 2022.
  • RFC 9113HTTP/2, June 2022 (consolidation + deprecations).
  • RFC 7540Hypertext Transfer Protocol Version 2 (HTTP/2), May 2015 (original, now obsoleted by 9113).
  • RFC 7541HPACK: Header Compression for HTTP/2, May 2015.
  • RFC 9110HTTP Semantics, June 2022.
  • RFC 9112HTTP/1.1, June 2022.
  • RFC 9218Extensible Prioritization Scheme for HTTP, June 2022.
  • RFC 9221An Unreliable Datagram Extension to QUIC, March 2022.
  • RFC 9250DNS over Dedicated QUIC Connections, May 2022.
  • RFC 9298Proxying UDP in HTTP (CONNECT-UDP / MASQUE), August 2022.
  • RFC 9369QUIC Version 2, May 2023.
  • RFC 9406HyStart++: Modified Slow Start for TCP, May 2023.
  • RFC 9460Service Binding and Parameter Specification via DNS (SVCB and HTTPS RRs), November 2023.
  • RFC 9484Proxying IP in HTTP, October 2023.
  • RFC 9618QUIC Acknowledgement Frequency, October 2024.
  • RFC 8446The Transport Layer Security (TLS) Protocol Version 1.3, August 2018.
  • RFC 8985RACK-TLP: A Time-Based Loss Detection Algorithm for TCP, February 2021.
  • RFC 8899Packetization Layer Path MTU Discovery for Datagram Transports, September 2020.
  • Langley et al. — “The QUIC Transport Protocol: Design and Internet-Scale Deployment”, SIGCOMM 2017. The Google production retrospective on gQUIC; still the most cited single QUIC paper.
  • Marx et al. — “Same Standards, Different Decisions: A Study of QUIC and HTTP/3 Implementation Diversity”, EPIQ workshop 2020. The paper introducing qlog + qvis.
  • Cardwell et al. — “BBR: Congestion-Based Congestion Control”, ACM Queue Sept-Oct 2016.
  • Ha, Rhee, Xu — “CUBIC: A New TCP-Friendly High-Speed TCP Variant”, ACM SIGOPS 2008.
  • Cloudflare blog — “HTTP/3” series 2019-2024 (Lucas Pardue, Junho Choi, Achiel van der Mandele, others); covers quiche internals, 0-RTT replay protection, anycast connection-ID routing, CPU optimization with io_uring + GSO/GRO.
  • Microsoft msquic docs — github.com/microsoft/msquic, especially the performance and Windows kernel integration notes.
  • Cloudflare Radar — radar.cloudflare.com, real-time HTTP version share + ECH + IPv6 adoption stats.
  • Internet-Draftsdraft-ietf-quic-multipath, draft-ietf-moq-transport, draft-ietf-tls-esni (ECH), all active 2024-2026.