HTTP/2 and HTTP/3 in Proxy Infrastructure: Frames, Streams, and the QUIC Problem
HTTP/1.1 was a protocol you could proxy by reading a line at a time and counting bytes. HTTP/2 and HTTP/3 are not. Both protocols introduce multiplexing, binary framing, and stateful header compression that force proxy implementers to either terminate and re-encode at each hop or accept significant functional limitations. This post covers the practical differences between HTTP/2 and HTTP/3 proxying, why some production systems still route traffic over HTTP/1.1 on purpose, and how QUIC changes the architecture of a proxy pool.
A Quick Refresher on Frames and Streams
HTTP/2 (RFC 9113) replaced the text protocol of HTTP/1.1 with a binary framing layer. A single TCP connection carries many concurrent streams, and each stream carries a sequence of frames: HEADERS, DATA, SETTINGS, WINDOW_UPDATE, RST_STREAM, and others. The key property is that frames from different streams can be interleaved on the wire. A 10 MB response for stream 5 will not block a 200-byte response for stream 7.
HTTP/3 (RFC 9114) keeps the concept of streams and frames but runs them over QUIC (RFC 9000) instead of TCP. QUIC is a UDP-based transport that provides its own congestion control, loss recovery, and per-stream flow control. The practical consequence for proxy implementation is that an HTTP/3 connection is not a TCP connection. You cannot forward it at layer 4 the way you forward a TCP CONNECT tunnel. There is no equivalent of SO_REUSEPORT that will let your kernel hand you a pre-authenticated QUIC stream.
HTTP/2 Header Compression: HPACK
HPACK (RFC 7541) is the header compression used by HTTP/2. It maintains a dynamic table on both sides of a connection that maps small integer indices to previously-seen header fields. When a client sends user-agent: Mozilla/5.0 ... once, subsequent requests on the same connection can refer to it by a two-byte index.
This creates a problem for proxies that want to inspect or modify headers. A proxy that decodes HPACK must maintain its own dynamic table, and the moment it terminates and re-encodes to a different upstream, the indices diverge. Most production HTTP/2 proxies (Envoy, HAProxy, nginx) handle this by fully terminating HTTP/2 at the proxy: they decode HPACK on the client side, hold the request in a canonical form, and re-encode HPACK on the upstream side with a fresh dynamic table.
Termination is expensive. A benchmark from Hex Proxies internal testing in April 2026 compared raw TCP CONNECT tunneling of HTTP/1.1 against full HTTP/2 termination on the same hardware. The HTTP/1.1 tunnel handled 14,200 requests per second per core. The HTTP/2 terminator handled 6,100 requests per second per core. The difference is HPACK state management, stream accounting, and flow control bookkeeping.
HTTP/3 Header Compression: QPACK
QPACK (RFC 9204) is HPACK redesigned for a protocol where streams can arrive out of order. HPACK updates the dynamic table in strict sequence, which works fine over TCP where bytes arrive in order but breaks over QUIC where stream 5 might arrive before stream 3. QPACK solves this by splitting the encoder state into a separate unidirectional stream and adding explicit insert-count tracking.
The practical impact is that QPACK encoders and decoders are more complex than HPACK, and the blocked-stream problem (a stream that references a table entry not yet received) requires careful handling. Off-the-shelf QUIC libraries such as quiche (Cloudflare), msquic (Microsoft), and quic-go cover this correctly, but writing one yourself is a 2,000-line-minimum undertaking.
Why You Can't Just CONNECT-Tunnel HTTP/3
A traditional HTTPS proxy works by accepting a CONNECT target:443 request from the client, opening a TCP connection to the target, and then splicing the two sockets together. The client's TLS handshake flows end-to-end and the proxy sees only opaque bytes. This is how almost all residential and ISP proxy traffic is carried today.
HTTP/3 breaks this model for three reasons. First, QUIC is UDP, and UDP has no connection state in the kernel. A proxy would have to maintain a five-tuple-to-connection map in userspace for every flow. Second, QUIC connection IDs (RFC 9000, Section 5.1) let a client migrate between network paths without tearing down the connection. A residential proxy that tunnels raw QUIC bytes would forward a client migration attempt to the origin, which would see the proxy's address disappear and a new address (the new client location) appear, likely terminating the session. Third, the QUIC handshake integrates TLS into the transport, and the initial packet is protected with a key derived from the connection ID. A naive UDP forwarder must rewrite the connection ID if it wants to multiplex multiple clients through a single socket, which requires decrypting and re-encrypting the initial packet.
The practical answer adopted by every major proxy operator in April 2026 is the same: proxies do not speak HTTP/3 to the client. They offer HTTP/1.1 and HTTP/2 over TLS, and when the client speaks HTTP/1.1 or HTTP/2 to the proxy, the proxy opens an HTTP/3 connection to the origin if the origin advertises it via the Alt-Svc header (RFC 7838). The client does not see HTTP/3. The origin sees HTTP/3.
MASQUE: The Standardized Answer
The IETF's MASQUE working group has defined CONNECT-UDP (RFC 9298) and CONNECT-IP (RFC 9484), which extend the HTTP CONNECT method to tunnel UDP datagrams and raw IP packets over HTTP/3. Apple's iCloud Private Relay uses MASQUE in production. MASQUE is the proper solution for carrying HTTP/3 end-to-end through a proxy.
MASQUE adoption in the commercial proxy market is still limited. Client library support is thin outside of Apple's platforms, and running a MASQUE proxy at residential-network scale requires rewriting the proxy control plane around HTTP/3. The Hex Proxies roadmap tracks MASQUE for ISP proxy exit nodes where the infrastructure is centrally managed.
When HTTP/1.1 Is Still the Right Choice
Three cases keep HTTP/1.1 in production for proxy workloads in 2026:
- Long-running scraping jobs with per-request rotation. If you rotate IP on every request, the multiplexing benefit of HTTP/2 disappears because each request is its own connection anyway. The HTTP/1.1 path is simpler and faster per connection.
- Clients that need deterministic ordering. HTTP/2 stream prioritization was deprecated in RFC 9218 and replaced with an extensible scheme that most origins ignore. If you need to ensure request A completes before request B, a single HTTP/1.1 connection with pipelining disabled is the predictable choice.
- Targets that fingerprint HTTP/2 SETTINGS frames. The initial SETTINGS frame a client sends (MAX_CONCURRENT_STREAMS, INITIAL_WINDOW_SIZE, HEADER_TABLE_SIZE) varies between browsers and HTTP clients, and Akamai's Bot Manager has hashed these for years. An HTTP/1.1 request has no such fingerprint surface.
A Code Example: Curl Over a Proxy to an HTTP/3 Origin
curl --http3 -x http://user:pass@proxy.hexproxies.com:8080 https://target.example/api
This command fails on most curl builds. The proxy speaks HTTP/1.1 CONNECT, the client speaks HTTP/3 to the target through the CONNECT tunnel, but the CONNECT tunnel is a TCP socket and HTTP/3 requires UDP. The working invocation is:
curl --http2 -x http://user:pass@proxy.hexproxies.com:8080 https://target.example/api
The client negotiates HTTP/2 with the target over the TCP tunnel. If the target serves an Alt-Svc header advertising HTTP/3, the proxy ignores it (it has no way to act on it) and the client ignores it for this request because the proxy path does not support HTTP/3.
Operational Implications for Proxy Buyers
If you are buying proxy capacity for a workload that targets HTTP/3 origins, the right question is not "does your proxy support HTTP/3." It is "how does the origin behave when it sees an HTTP/2 request after advertising HTTP/3." Most origins fall back to HTTP/2 gracefully. A few (notably some Cloudflare customers with HTTP/3-only configurations) do not. Test your target workload against both paths before committing.
For high-throughput price monitoring and SEO monitoring pipelines, the HTTP/1.1-over-CONNECT path remains the most predictable. HTTP/2 termination buys you multiplexing if you have very many small requests to the same origin on the same connection, which is rare in rotating-proxy workloads.
Conclusion
HTTP/2 and HTTP/3 are significant protocol upgrades for origins. For proxies, they are significant complications. An HTTP/2 proxy has to decode and re-encode HPACK, track stream state, and manage flow control windows. An HTTP/3 proxy has to do all that plus operate over UDP with connection-ID rewriting and an integrated TLS handshake. Until MASQUE is broadly deployed, the practical answer for nearly all commercial proxy traffic is to terminate at HTTP/2 or HTTP/1.1 at the proxy and let the proxy speak whichever protocol the origin prefers. That is not a workaround, it is the current state of the art.