Capture Real Client IP Behind Multiple Proxies Using HAProxy & Nginx Ingress
This guide explains how to retrieve the true client IP in environments with multiple proxy layers by understanding remote_addr, X-Forwarded-For, and X-Real-IP headers, and configuring HAProxy and Nginx Ingress with appropriate options to forward and preserve the original IP.
Background Information
Because the product requires obtaining the real client IP on the application side, the request chain passes through multiple proxies, making it impossible to get the true IP by default.
Basic Concepts
Before implementation, understand the common headers used to obtain IP addresses.
remote_addr
X-Forwarded-For
X-Real-IP
(1) remote_addr
remote_addrrepresents the client IP, but its value is set by the server based on the client’s connection. If there is no proxy,
remote_addris the host IP. When a proxy is present,
remote_addrbecomes the proxy’s IP unless the proxy manually sets it to the original client IP.
(2) X-Forwarded-For
X-Forwarded-For(XFF) is an HTTP extension header. Its value consists of multiple IPs separated by commas and spaces, ordered from the farthest device to the nearest proxy, e.g.,
X-Forwarded-For: client,proxy1,proxy2. Note that the XFF header can be forged.
PS: The format of X-Forwarded-For can be spoofed.
If an application sits behind three proxies (Proxy1, Proxy2, Proxy3) with IPs IP1, IP2, IP3 and the real client IP is IP0, the XFF header received by the application should be:
X-Forwarded-For: IP0,IP1,IP2. IP3 is omitted because Proxy3 forwards Proxy2’s request, adding Proxy2’s IP to XFF, while the client IP is placed in
remote_addr.
PS: Only one proxy adds the previous node’s IP to XFF .
(3) X-Real-IP
X-Real-IPis a custom header usually set by HTTP proxies to indicate the IP of the device that established the TCP connection. Unlike XFF, it is not a list; each proxy replaces the previous value instead of appending.
Ideally, the application should receive
X-Real-IPas the client’s real IP, meaning only the first proxy sets this header while subsequent proxies simply forward the request.
Implementation Details
In practice, the first layer (SLB) is a pure TCP proxy, so no extra configuration is needed there; the client IP is passed through directly.
When the request reaches HAProxy, we need to add the client IP to
XFFand set
X-Real-IPto the client IP. The relevant HAProxy configuration is:
<code>defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
log 127.0.0.1 local3
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 5m
timeout server 5m
timeout http-keep-alive 10s
timeout check 10s
unique-id-format %{+X}o %ci%cp%fi%fp%Ts%rt%pid
frontend https_link_ha
bind *:443 ssl crt /usr/local/etc/haproxy/cert/crt/ ca-file /usr/local/etc/haproxy/cert/ca/ca.pem verify optional
mode http
log-format "%ID %ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
option accept-invalid-http-request
http-request set-header x-request-id %[unique-id]
http-request set-header x-request-time %[date()]
http-request set-header X-Real-IP %[src]
default_backend pre
backend pre
server 1 10.74.136.13:8080 check inter 1500 rise 3 fall 3 weight 3</code>The two key directives are:
option forwardfor except 127.0.0.0/8– adds the client’s IP to the
XFFheader.
http-request set-header X-Real-IP %[src]– sets
X-Real-IPto the client’s IP.
After HAProxy, the request reaches the Ingress layer, which also receives the client IP in
XFF. By default, Nginx Ingress does not enable XFF forwarding, so we must configure it via its ConfigMap:
<code>use-forwarded-headers: 'true'
compute-full-forwarded-for: 'true'
</code>Applying these settings causes Nginx Ingress to reload automatically without a manual restart, and the application logs will now contain the true client IP.
Note that in some scenarios, such as when a CDN sits in front of the SLB, the IP obtained via
XFFmay be the CDN’s source IP rather than the end‑user’s IP.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.