MASQUE
MASQUE (Multiplexed Application Substrate over QUIC Encryption) is a framework for proxying and tunneling arbitrary traffic over HTTP. It carries UDP and IP packets inside an HTTP connection using the extended CONNECT method, HTTP Datagrams, and the Capsule Protocol, so tunneled traffic shares a single HTTP/2 or HTTP/3 connection alongside ordinary web requests.
What MASQUE is
MASQUE is a set of HTTP extensions that turn an HTTP server into a proxy for non-HTTP traffic. The name stands for Multiplexed Application Substrate over QUIC Encryption. Rather than defining a new protocol on a new port, MASQUE reuses the HTTP machinery already running on port 443. A client opens a tunnel by sending an extended CONNECT request, and the proxy relays the client's UDP datagrams or IP packets to their destination.
The framework rests on three building blocks. Proxying UDP
in HTTP defines a connect-udp tunnel for single UDP flows.
Proxying IP in HTTP defines a connect-ip tunnel for raw IP
packets. Both rely on HTTP Datagrams and the Capsule
Protocol, which give HTTP a way to move datagram-style
payloads through a request stream. Together these pieces let
a proxy forward QUIC, DNS, full VPN traffic, or any other
packet flow over the same connection that serves regular
pages.
Why MASQUE exists
MASQUE exists to move proxying and VPN-style tunneling onto modern HTTP transport so tunneled traffic blends in with normal web traffic. Older tunneling approaches run on dedicated ports and protocols that network middleboxes recognize and sometimes block. MASQUE traffic looks like an HTTPS connection, because that is what it is. The tunnel rides inside HTTP/2 or HTTP/3, encrypted by TLS or QUIC, multiplexed with other streams on the same connection.
The practical driver is privacy proxying at scale. Apple iCloud Private Relay uses MASQUE to send Safari traffic through a two-hop relay, hiding the client IP from the destination while hiding the destination from the first relay. Running the tunnel over HTTP/3 means it survives network changes, benefits from QUIC connection migration, and avoids the head-of-line blocking that affects tunnels built on a single TCP stream. Multiplexing also matters: one QUIC connection can carry many independent tunnels plus ordinary requests at once.
MASQUE and OHTTP
MASQUE and Oblivious HTTP both serve privacy goals but operate at different levels. MASQUE creates a persistent connection-level tunnel for all traffic, similar to a VPN. OHTTP encrypts and routes individual request-response pairs. A single server can act as a MASQUE proxy and an OHTTP relay at the same time.
The extended CONNECT method
MASQUE tunnels start with the extended CONNECT
method, which adds a :protocol pseudo-header to the
classic CONNECT request. The original CONNECT method
establishes a TCP tunnel to a host and port named in the
request target. Extended CONNECT keeps the CONNECT method
but introduces :protocol to select what kind of tunnel to
build. For MASQUE, that value is connect-udp or
connect-ip.
Extended CONNECT requires HTTP/2 or HTTP/3. The
server advertises support by sending the
SETTINGS_ENABLE_CONNECT_PROTOCOL setting, the same setting
that enables WebSocket over HTTP/2 and HTTP/3. A
MASQUE request carries a full set of pseudo-headers rather
than a bare authority:
| Pseudo-header | Role |
|---|---|
:method |
Always CONNECT |
:protocol |
connect-udp or connect-ip |
:scheme |
The request scheme, usually https |
:authority |
The proxy's host and port |
:path |
The expanded tunnel path on the proxy |
The :path comes from a URI template the client configures
ahead of time. Template variables fill in the tunnel target,
which keeps the proxy endpoint discoverable through a
well-known URI while letting the path
encode where the traffic should go.
HTTP Datagrams and the Capsule Protocol
HTTP Datagrams are a convention for carrying multiplexed, possibly unreliable datagrams inside an HTTP connection. A MASQUE tunnel needs to move packet-style data that does not fit the request-response model, and HTTP Datagrams provide that channel. Each datagram begins with a context ID encoded as a variable-length integer, followed by the payload. Context ID zero carries the tunneled UDP or IP payload, and other context values are reserved for extensions.
The Capsule Protocol carries this data and related control
messages through the request stream. A capsule is a
type-length-value tuple: a variable-length integer type, a
length, and a value. Capsules flow in the message content
after a 101 or
2xx response, in both directions, for
the life of the tunnel. The Capsule-Protocol header field,
set to the Boolean value ?1, signals that the stream
content uses the Capsule Protocol so intermediaries handle
it correctly. Receivers silently drop capsule types they do
not recognize, and intermediaries forward unknown types
unchanged, which keeps the protocol extensible.
The transport mapping differs by HTTP version. HTTP/3
runs over QUIC and uses native QUIC DATAGRAM frames for
unreliable delivery, enabled through the
SETTINGS_H3_DATAGRAM setting. In HTTP/3 a datagram
references its tunnel using a quarter stream ID, the stream
ID divided by four. HTTP/2 has no unreliable datagram
support, so the Capsule Protocol carries the datagrams as
reliable, ordered DATAGRAM capsules instead. The Capsule
Protocol applies only to extended CONNECT tunnels, which
keeps web platform APIs from reaching capsule-based
protocols directly.
Proxying UDP in HTTP
Proxying UDP in HTTP defines the connect-udp tunnel, which
relays a single UDP flow between a client and a target host
and port. The client sends an extended CONNECT
request with :protocol set to connect-udp and a :path
that names the target. After the proxy returns a
2xx response, each UDP packet travels
as an HTTP Datagram with context ID zero. The proxy strips
the framing and forwards the UDP payload to the target, then
relays return packets back through the tunnel.
The default URI template encodes the target host and port as template variables:
https://proxy.example.re:443/.well-known/masque/udp/{target_host}/{target_port}/
A client tunneling to 192.0.2.6 on port 443 expands the
template and sends the request to that path. The
connect-udp tunnel is the common path for proxying QUIC
and HTTP/3 traffic, since QUIC itself runs on UDP. A
client behind a MASQUE proxy reaches an HTTP/3 origin by
wrapping its QUIC packets in connect-udp datagrams.
Proxying IP in HTTP
Proxying IP in HTTP defines the connect-ip tunnel, which
relays full IP packets rather than a single transport flow.
The client sends an extended CONNECT request
with :protocol set to connect-ip. Once the tunnel opens,
complete IP packets travel as HTTP Datagrams with context ID
zero, letting the tunnel carry any IP-based protocol at once.
This is the basis for remote-access VPNs, site-to-site VPNs,
and general packet forwarding over HTTP.
The default template uses optional target and protocol variables, which a wildcard fills when the client wants an unrestricted tunnel:
https://proxy.example.re:443/.well-known/masque/ip/{target}/{ipproto}/
IP proxying adds control capsules that UDP proxying does not
need, because an IP tunnel often has to configure addressing
and routing. The ADDRESS_ASSIGN capsule lets one endpoint
assign its peer a list of IP addresses or prefixes to use as
source addresses. The ROUTE_ADVERTISEMENT capsule
communicates which address ranges an endpoint will route
traffic toward. These capsules set up a working tunnel
in-band, so a client that starts with no network
configuration receives an address and routes through the
tunnel itself.
UDP proxying vs IP proxying
The difference between connect-udp and connect-ip comes
down to the layer each tunnel operates on. UDP proxying
relays one named UDP flow to a specific target host and
port. IP proxying relays whole IP packets and can carry many
flows and protocols across a single tunnel.
| Property | connect-udp | connect-ip |
|---|---|---|
| Layer | UDP flow | IP packets |
| Target | One host and port | One or more hosts, optionally any |
| Payload | UDP payload | Full IP packet |
| Control capsules | None required | ADDRESS_ASSIGN, ROUTE_ADVERTISEMENT |
| Typical use | Proxying QUIC and HTTP/3 | VPN-style full tunneling |
UDP proxying suits a client that needs to reach one UDP service, most often a QUIC or HTTP/3 origin behind the proxy. IP proxying suits full-tunnel scenarios where the client routes all of its traffic through the proxy and needs an assigned address. Both run on the same HTTP Datagram and Capsule Protocol foundation, so a proxy can offer either over the same HTTP connection.
Example
A client establishes a connect-udp tunnel to a target UDP
service through a MASQUE proxy over HTTP/2. The client
has already configured the proxy's URI template and expands
it with the target host 192.0.2.6 and port 443. The
request uses extended CONNECT, so the
:protocol pseudo-header selects the tunnel type and the
:path carries the expanded
well-known URI. The capsule-protocol
field set to ?1 marks the stream as Capsule Protocol
content.
Request
HEADERS
:method = CONNECT
:protocol = connect-udp
:scheme = https
:authority = proxy.example.re
:path = /.well-known/masque/udp/192.0.2.6/443/
capsule-protocol = ?1
The proxy accepts the tunnel with a 2xx response. After this point the HTTP stream carries HTTP Datagrams rather than a normal response body.
Response
HEADERS
:status = 200
capsule-protocol = ?1
Once the tunnel is open, each UDP packet travels as an HTTP
Datagram with context ID zero. The proxy forwards the UDP
payload to 192.0.2.6:443 and relays return packets back to
the client. The same connection continues to serve other
HTTP/2 streams in parallel, so the tunnel multiplexes
alongside ordinary requests.
See also
- RFC 9298: Proxying UDP in HTTP
- RFC 9484: Proxying IP in HTTP
- RFC 9297: HTTP Datagrams and the Capsule Protocol
- RFC 9000: QUIC
- CONNECT
- CONNECT-TCP
- Protocol upgrade
- Oblivious HTTP
- HTTP connection
- Well-known URIs
- HTTP/2
- HTTP/3
- WebSocket
- HTTP headers