NaïveProxy applies a padding protocol on top of HTTP/2 CONNECT tunnels to mitigate length-based traffic analysis. The protocol is intentionally lightweight and improvised rather than cryptographically elaborate — the design philosophy holds that a large number of simple, distinct circumvention protocol designs creates a better logistical barrier against censorship research than a small number of sophisticated ones. What follows is an informal specification of the protocol as implemented.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/klzgrad/naiveproxy/llms.txt
Use this file to discover all available pages before exploring further.
Design Philosophy
The padding protocol opts for low overhead and easier implementation. The authors believe that the proliferation of expendable, improvised circumvention protocol designs is a better logistical impediment to censorship research than a small number of sophisticated designs. Simplicity also reduces implementation surface area and makes the protocol easier to audit and port.Proxy Payload Padding
NaïveProxy proxies bidirectional streams through HTTP/2 (or HTTP/3) CONNECT tunnels. Within each such stream, the firstkFirstPaddings = 8 reads and writes after the stream is established are padded. The padded format is:
padding_sizeis a random integer uniformly distributed in[0, kMaxPaddingSize]wherekMaxPaddingSize = 255.original_data_sizecannot exceed 65535. If the payload is larger, it must be split into multiple reads or writes, each wrapped in its ownPaddedData.- Reads and writes after the first 8 are transmitted without padding to avoid ongoing performance overhead. Later packet lengths are generally considered less informative for traffic analysis.
Why 8 padded exchanges?
The value 8 was chosen to cover the complete initial handshake sequences for both sides of the connection, flattening the packet length distribution spikes that arise from predictable protocol preambles.Common client initial sequence
Common client initial sequence
- TLS ClientHello
- TLS ChangeCipherSpec + Finished
- H2 Magic + SETTINGS + WINDOW_UPDATE
- H2 HEADERS GET
- H2 SETTINGS ACK
Common server initial sequence
Common server initial sequence
- TLS ServerHello + ChangeCipherSpec + …
- TLS Certificate + …
- H2 SETTINGS
- H2 WINDOW_UPDATE
- H2 SETTINGS ACK
- H2 HEADERS 200 OK
kFirstPaddings = 8 ensures that all predictable initial packets carry random padding before any unpadded traffic appears.
H2 RST_STREAM Frame Padding
In practice, NaïveProxy tends to send more RST_STREAM frames per session than a regular browser would. An unusually high RST_STREAM rate is a detectable behavioral signal. To mitigate this, an END_STREAM DATA frame padded to a total length uniformly distributed in[48, 72] bytes is prepended to each RST_STREAM frame, making the pair resemble a HEADERS frame from the outside. The server often replies to this DATA frame with a WINDOW_UPDATE, because padding bytes are counted against HTTP/2 flow control.
Whether the server’s WINDOW_UPDATE reply itself constitutes a new detectable pattern is an open question acknowledged in the original specification.
H2 HEADERS Frame Padding
CONNECT request and response HEADERS frames are typically shorter than regular browser HEADERS frames and appear at lower frequency, making them identifiable by length alone. To normalize their length, apadding header is added to both request and response HEADERS frames. The padding value is filled with pseudo-random symbols chosen specifically to avoid Huffman coding (which would reduce length) and to avoid being indexed in the HPACK dynamic table (which would affect future frames).
| Frame | Padding length distribution |
|---|---|
| CONNECT request | Uniform random in [16, 32] bytes |
| CONNECT response | Uniform random in [30, 62] bytes |
Opt-In Negotiation
The padding protocol is opt-in and fully interoperable in both directions:- A NaïveProxy client connecting to a standard HTTP/2 proxy that does not support padding will work correctly — padding is simply not activated.
- A NaïveProxy server receiving a connection from a standard HTTP/2 client (such as a regular Chrome browser) will work correctly — it will not expect or require padding.
- The client includes the
paddingheader in its CONNECT request. - The server includes the
paddingheader in its CONNECT response. - Only if both headers are present does either side apply the padding protocol for subsequent payload.