Client IP preservation
Enable the PROXY protocol
When the load balancer proxies a TCP connection, it overwrites the client’s source IP address with its own when communicating with the backend server. The PROXY protocol adds a header to a TCP connection to preserve the client’s IP address. This method solves the lost-client-IP problem for any application-layer protocol that transmits its messages over TCP/IP. To work, both the sender and receiver must support the protocol and have it enabled.
The load balancer adds the header to TCP connections before relaying them to upstream servers. When placed behind another proxy, it can also receive the PROXY protocol header attached to the incoming connection. This feature supports IPv4 and IPv6 addresses.
Receive the PROXY protocol Jump to heading
To accept a PROXY protocol header on incoming TCP connections:
-
Add an
accept-proxy
argument to thebind
line in afrontend
section. This argument detects both PROXY protocol version 1 (text format) and PROXY protocol version 2 (binary format).The example below accepts the PROXY protocol header from incoming connections:
haproxyfrontend mywebsitebind :80 accept-proxydefault_backend webservershaproxyfrontend mywebsitebind :80 accept-proxydefault_backend webservers
Send the PROXY protocol Jump to heading
To send a PROXY protocol version 1 header (text format) to the backend servers:
-
Add a
send-proxy
argument to theserver
lines in abackend
section:haproxybackend webserversbalance roundrobinserver s1 192.168.56.20:3000 check send-proxyserver s2 192.168.56.21:3000 check send-proxyhaproxybackend webserversbalance roundrobinserver s1 192.168.56.20:3000 check send-proxyserver s2 192.168.56.21:3000 check send-proxy
To send a PROXY protocol version 2 header (binary format) to the backend servers:
-
Add a
send-proxy-v2
argument to theserver
lines in abackend
section:haproxybackend webserversbalance roundrobinserver s1 192.168.56.20:3000 check send-proxy-v2server s2 192.168.56.21:3000 check send-proxy-v2haproxybackend webserversbalance roundrobinserver s1 192.168.56.20:3000 check send-proxy-v2server s2 192.168.56.21:3000 check send-proxy-v2
PROXY protocol on AWS Network Load Balancers Jump to heading
AWS NLB can use the PROXY protocol to set the IP address in requests. Enable this feature as described here:
Then add the keyword accept-proxy
to your bind line, as described in the section Receive the PROXY protocol. The requests will then automatically have the right source IP address.
Send metadata in TLV format Jump to heading
This section applies to:
- HAProxy 2.9 and newer
- HAProxy ALOHA 16.5 and newer
- HAProxy Enterprise 2.9r1 and newer
While the PROXY protocol’s primary function is to preserve a client’s source IP address when traffic traverses one or more reverse proxies, it can also serve as a way to attach metadata to TCP connections. In the PROXY Protocol v2 (binary header format), Type-Length-Value (TLV) vectors store variable-length data in the PROXY protocol header, allowing you to transmit information relevant to the sender and receiver.
Here are a few use cases for TLV vectors:
- Azure’s Private Link platform enables you to connect your on-premises network to Azure services without exposing traffic to the Internet. With HAProxy running on-premises, you can receive messages over the Private Link while preserving the client’s IP address, as Azure supports the PROXY protocol header. Azure will also send a TLV vector that contains additional connection information, such as the LinkID of the private endpoint, helping to uniquely identify consumers.
- AWS Network Load Balancer can serve as a layer 4 load balancer that distributes traffic to multiple HAProxy instances running in the cloud or on-premises. Enable the PROXY protocol in AWS Network Load Balancer to preserve the client’s IP address as it passes through Network Load Balancer. In addition, when traffic to the Network Load Balancer comes through a VPC endpoint service, AWS Network Load Balancer adds a TLV vector that contains the VPC endpoint ID.
TLV format Jump to heading
TLV vectors have the following format:
- The first byte is the type of vector.
- The second two bytes represent the length in bytes of the value (not including the type and length bytes).
- The last part is a variable number of bytes, the length specified by the length field, containing the value.
The PROXY protocol defines several types, which you can learn more about in the PROXY protocol specification:
#define PP2_TYPE_ALPN 0x01#define PP2_TYPE_AUTHORITY 0x02#define PP2_TYPE_CRC32C 0x03#define PP2_TYPE_NOOP 0x04#define PP2_TYPE_UNIQUE_ID 0x05#define PP2_TYPE_SSL 0x20#define PP2_SUBTYPE_SSL_VERSION 0x21#define PP2_SUBTYPE_SSL_CN 0x22#define PP2_SUBTYPE_SSL_CIPHER 0x23#define PP2_SUBTYPE_SSL_SIG_ALG 0x24#define PP2_SUBTYPE_SSL_KEY_ALG 0x25#define PP2_TYPE_NETNS 0x30
#define PP2_TYPE_ALPN 0x01#define PP2_TYPE_AUTHORITY 0x02#define PP2_TYPE_CRC32C 0x03#define PP2_TYPE_NOOP 0x04#define PP2_TYPE_UNIQUE_ID 0x05#define PP2_TYPE_SSL 0x20#define PP2_SUBTYPE_SSL_VERSION 0x21#define PP2_SUBTYPE_SSL_CN 0x22#define PP2_SUBTYPE_SSL_CIPHER 0x23#define PP2_SUBTYPE_SSL_SIG_ALG 0x24#define PP2_SUBTYPE_SSL_KEY_ALG 0x25#define PP2_TYPE_NETNS 0x30
In addition, 16 type values are reserved for application-specific data, such as those used by Azure and AWS.
#define PP2_TYPE_MIN_CUSTOM 0xE0#define PP2_TYPE_MAX_CUSTOM 0xEF
#define PP2_TYPE_MIN_CUSTOM 0xE0#define PP2_TYPE_MAX_CUSTOM 0xEF
A range of 8 type values are reserved for temporary, experimental use by application developers and protocol designers. You shouldn’t use these in production:
#define PP2_TYPE_MIN_EXPERIMENT 0xF0#define PP2_TYPE_MAX_EXPERIMENT 0xF7
#define PP2_TYPE_MIN_EXPERIMENT 0xF0#define PP2_TYPE_MAX_EXPERIMENT 0xF7
Receive TLV fields Jump to heading
Use the fc_pp_tlv
fetch method to receive TLV vectors from a PROXY protocol header. The fetch returns a string type.
Azure example:
haproxy
frontend fe_mainbind :80 accept-proxytcp-request content set-var(sess.azure_vpcid) fc_pp_tlv(0xEE),bytes(1) if { fc_pp_tlv(0xEE),bytes(0,1),hex eq 01 }
haproxy
frontend fe_mainbind :80 accept-proxytcp-request content set-var(sess.azure_vpcid) fc_pp_tlv(0xEE),bytes(1) if { fc_pp_tlv(0xEE),bytes(0,1),hex eq 01 }
AWS example:
haproxy
frontend fe_mainbind :80 accept-proxytcp-request content set-var(sess.aws_linkid) fc_pp_tlv(0xEA),bytes(1) if { fc_pp_tlv(0xEA),bytes(0,1),hex eq 01 }
haproxy
frontend fe_mainbind :80 accept-proxytcp-request content set-var(sess.aws_linkid) fc_pp_tlv(0xEA),bytes(1) if { fc_pp_tlv(0xEA),bytes(0,1),hex eq 01 }
Send TLV fields Jump to heading
Add the set-proxy-v2-tlv-fmt
argument to server
directives to send TLV fields to a backend server. This argument must be accompanied by the send-proxy-v2
argument.
Azure example:
haproxy
backend webappserver srv1 192.168.1.10:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0xEE) %[fc_pp_tlv(0xEE)]
haproxy
backend webappserver srv1 192.168.1.10:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0xEE) %[fc_pp_tlv(0xEE)]
AWS example:
haproxy
backend webappserver srv1 192.168.1.10:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0xEA) %[fc_pp_tlv(0xEA)]
haproxy
backend webappserver srv1 192.168.1.10:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0xEA) %[fc_pp_tlv(0xEA)]
See also Jump to heading
Do you have any suggestions on how we can improve the content of this page?