A Quick Look at How Proxies and Tunnels Work

Forward proxying is a way to create a protected end-to-end tunnel between a client and a target server, with a proxy server in the middle relaying traffic. This is different from a reverse proxy such as a CDN, which terminates client connections and takes on responsibilities like caching, web application firewall (WAF) enforcement, and load balancing. In a forward proxy setup, the details of the tunnel—how it is set up, how it is used, and whether it offers authenticated encryption—depend on the specific proxy protocol.

The most common tunnel on the Internet is TCP, a transport protocol that provides a reliable, ordered byte stream. Applications read and write to this abstract stream, while TCP handles the messy parts under the hood: splitting the data into packets, sending them in order, and reassembling them at the receiving end. If a packet is lost or reordered, TCP takes care of retransmission without bothering the application. The catch is that when a lost packet is detected, the sender must retransmit it, which delays the reconstruction of the byte stream. This is known as TCP head-of-line blocking. Applications using the socket API often cannot tell whether a delay comes from a slow remote peer or from packet loss in the network.

There are several established options for proxying TCP end-to-end:

  • SOCKS: a cleartext protocol that requires a relatively expensive connection establishment step.
  • Transparent TCP proxies (PEPs): must sit directly on the network path, offer no extra transport security, and are by definition limited to TCP.
  • Layer 4 proxies (e.g., Cloudflare Spectrum): may rely on out-of-band metadata such as the PROXY protocol.
  • HTTP CONNECT: converts an HTTPS connection into an opaque byte stream.

When looking for a reusable, general-purpose protocol with well-defined abstractions for future systems, the IETF selected HTTP as the substrate via its CONNECT method. HTTP/1.1 and HTTP/2 have long supported proxying TCP-based protocols, and the pattern extends to the newly standardized HTTP/3.

CONNECT in HTTP/1.1 and HTTP/2

With HTTP/1.1, the CONNECT method establishes an end-to-end TCP tunnel between a client and a target server via a proxy. A client sends a CONNECT request asking the proxy to open a TCP connection to a given host and port. If the proxy succeeds, it answers with a 2xx status code; an error appears as a 5xx response. Once the tunnel is up, there are two independent TCP connections—one on each side of the proxy—and either can be terminated to stop the flow.

It is worth noting that these proxies forward the data on the logical byte stream, not the TCP packets themselves. Nothing stops an intermediary from inspecting or altering plaintext data, but CONNECT is typically used to create an end-to-end TLS session. In that case, the proxy only sees encrypted TLS records and cannot decrypt content without the keys. Also, after a successful CONNECT request in HTTP/1.1, the whole connection becomes a tunnel; no further HTTP messages can be sent to the proxy on that connection.

HTTP/2 changes the picture by multiplexing logical streams over a single TCP connection. Each request and response runs on its own stream, identified by a frame header. Frames can be large, so they may span multiple TLS records or TCP segments. Because streams are independent and reliable, concurrent streams can make progress at different rates.

HTTP/2 supports the CONNECT method as well, but unlike HTTP/1.1 it does not take over the entire connection—it turns just one stream into a tunnel. If the proxy can reach the target, it responds with a 2xx status. After that, the client sends DATA frames whose contents the proxy forwards in TCP packets; the proxy reads from the target and populates DATA frames in the return direction. To close a tunnel, you terminate the one stream, leaving the HTTP/2 connection intact. This allows a single connection to host multiple CONNECT tunnels, which reduces the total number of TCP connections and lets related tunnels share fate when either side needs an orderly shutdown. The proxy-to-server side, however, still requires one independent TCP connection per tunnel.

Multiplexing tunnels on concurrent streams raises the question of prioritization, which the IETF HTTP Working Group has been addressing in draft proposals for extensible priorities. The behavior for CONNECT tunnels differs from page-load prioritization, and those differences are captured in the draft.

QUIC and HTTP/3

QUIC is a secure, multiplexed transport protocol standardized as RFC 9000 in May 2021. It provides reliable, ordered streams at the transport layer, and these streams are the only QUIC primitive that can carry application data. QUIC has no opinion about how those streams are used; the application mapping must define that.

QUIC stream data is transmitted in STREAM frames, which must each fit entirely inside a single QUIC packet. QUIC packets, in turn, must each fit within a UDP datagram since fragmentation is not allowed. As a result, long streams get serialized into packets sized to the path MTU. Reliability works a bit differently than TCP: QUIC acknowledges frames, not packets, and if the sender detects a lost frame it must retransmit that data, deciding for itself how to repacketize and reschedule it. Because different packets can carry STREAM frames belonging to different streams, one stream can stall and retransmit without affecting the others—thus avoiding the head-of-line blocking seen with TCP. Even if a UDP datagram containing data for one stream is lost, the remaining streams can continue.

HTTP/3 is an application mapping for QUIC that defines how its own frames (HEADERS, DATA, and so on) ride over QUIC streams. It deals with settings, QPACK state, and requests and responses. Since QUIC handles the messy transport details, HTTP/3 is simpler than HTTP/2, and its frames can be arbitrarily sized; the QUIC layer takes care of segmenting them before sending. HTTP/3 also offers the CONNECT method with the same semantics as HTTP/2: each request stream becomes a standalone end-to-end tunnel.

Approaching Limitations

HTTP CONNECT is a simple, flexible primitive with many uses, especially for privacy-oriented technology. It can proxy DNS over HTTPS—as used by Oblivious DoH—or generic HTTPS traffic, whether that is based on HTTP/1.1 or HTTP/2.

But CONNECT assumes a TCP target. What about proxying QUIC itself, or entire IP datagrams, as VPN technologies like IPsec or WireGuard do? Those use cases are where MASQUE comes in. The IETF’s MASQUE Working Group is standardizing methods for proxying datagram-based protocols such as UDP and IP on top of HTTP/3.