PROXY protocol decoder (v1 and v2)
Paste hex bytes or a v1 text header and get a field by field decode, including TLVs, TLS details and the length checks that catch a malformed sender.
Decode a PROXY protocol header
What the header looks like on the wire#
The PROXY protocol prepends a small block to the front of a TCP connection, before any application bytes, carrying the original source and destination. It exists because an L4 proxy cannot add an HTTP header, so when TLS is passed through untouched or the protocol is not HTTP at all, there is nowhere else to put the client address.
Two versions are in use.
| v1 | v2 | |
|---|---|---|
| Encoding | ASCII text, CRLF terminated | Binary |
| Maximum size | 107 bytes | 16 byte prefix plus a declared length |
| Starts with | PROXY | the 12 byte signature 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a |
| Extra metadata | none | TLVs: ALPN, authority (SNI), unique ID, TLS details, CRC32C |
| Detects a non-speaking peer | poorly | the signature is chosen so HTTP, TLS and most parsers reject it outright |
| Use it when | a receiver only supports v1 | everything else |
The v2 signature is not arbitrary. It was picked so that a receiver that does not understand the protocol, or a request that arrives without the header, fails loudly and immediately rather than being interpreted as an HTTP request.
Reading a v2 header by hand#
0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a signature, 12 bytes, always this
21 version 2 (high nibble), PROXY command (low nibble)
11 AF_INET (high nibble), STREAM/TCP (low nibble)
00 17 23 more bytes follow
cb 00 71 07 source address 203.0.113.7
0a 00 03 0e destination address 10.0.3.14
d3 26 source port 54054
01 bb destination port 443
05 00 08 72 65 71 2d 31 32 33 34 TLV: PP2_TYPE_UNIQUE_ID, 8 bytes, "req-1234"A command byte of 0x20 instead of 0x21 means LOCAL: the connection comes from the proxy itself rather than a proxied client, which is what health checks send. A receiver must ignore any address block on a LOCAL header and use the real socket addresses. Treating a health check as a proxied connection from 0.0.0.0:0 is a common bug in home grown parsers.
The operational rule that matters most#
The mirror image is just as disruptive: a listener expecting the header that receives a plain connection will fail. In nginx that produces an error-log line of the form broken header: "<the bytes it read>" while reading PROXY protocol. In HAProxy the connection is dropped. This is why a health checker pointed at a PROXY protocol port must also speak the protocol, and why enabling it is never a one sided change.
Full configuration for both ends, plus the support matrix across HAProxy, nginx, Envoy, AWS NLB, stunnel and the database proxies, is in the PROXY protocol reference.
Frequently asked questions#
How do I tell v1 from v2?#
Look at the first bytes. v1 is the ASCII text PROXY . v2 is the 12 byte binary signature starting 0d 0a 0d 0a 00 0d 0a. A receiver distinguishes them the same way, which is why both can be accepted on one listener.
What are the TLVs used for?#
They carry metadata a bare address pair cannot: the negotiated ALPN, the SNI the client sent (the authority TLV), a unique connection identifier for tracing, and TLS details including the client certificate subject and the verification result. That last group is how a TLS terminating L4 proxy passes client certificate identity to a backend without an HTTP header.
Does the PROXY protocol work with UDP?#
The transport nibble can indicate DGRAM, and the specification allows it, but support is far less common than for TCP. Check your specific sender and receiver rather than assuming.
Why does my backend see 0.0.0.0 as the client?#
Most likely a LOCAL command header, which carries no meaningful addresses, or a parser that read the address block from a LOCAL header anyway. Decode the actual bytes above and check the command nibble.
Can I use the PROXY protocol and X-Forwarded-For together?#
Yes, and in a chain with an L4 hop in front of an L7 hop that is normal. The L4 hop sends the PROXY protocol, the L7 hop reads it to set its own notion of the peer, and then appends X-Forwarded-For for the application. The trust configuration has to be right at both hops.
Primary sources#
Every normative claim on this page is checked against the specification or the vendor documentation listed here. Where behaviour is version dependent, the version is named in the text.