When implementing HAProxy as a load balancer, one common frustration emerges: all backend servers start seeing requests as originating from the HAProxy IP rather than the actual client IP. This breaks several critical functions:
# Problematic Apache logs showing HAProxy IP instead of client IP
192.168.1.100 - - [15/Jan/2023:10:22:45 +0000] "GET /secure/ HTTP/1.1" 403 298
The solution requires two key modifications to your HAProxy configuration:
frontend http-in
bind *:80
option forwardfor header X-Forwarded-For
option forwardfor except 127.0.0.1
http-request set-header X-Forwarded-For %[src]
default_backend servers
backend servers
server app1 10.0.0.1:80 check
server app2 10.0.0.2:80 check
For Apache servers, enable the remoteip module:
# Apache configuration
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 192.168.1.100 # HAProxy IP
RemoteIPInternalProxy 10.0.0.0/24 # Private network
For Nginx:
# Nginx configuration
real_ip_header X-Forwarded-For;
set_real_ip_from 192.168.1.100;
set_real_ip_from 10.0.0.0/24;
Test with curl to confirm IP forwarding works:
curl -H "X-Forwarded-For: 203.0.113.45" http://yourdomain.com/ip-test
For security-sensitive environments, consider these additional measures:
# Restrict IP spoofing
acl valid_ip hdr_ip(X-Forwarded-For) -m reg -i ^([0-9]{1,3}\.){3}[0-9]{1,3}$
http-request deny if !valid_ip
# Rate limiting by real IP
stick-table type ip size 1m expire 1h store http_req_rate(10s)
tcp-request content track-sc0 src
When implementing HAProxy as a reverse proxy for web applications, one common challenge is maintaining the original client IP addresses. By default, backend servers only see HAProxy's IP address, which breaks IP-based functionality in applications and security configurations.
The crucial configuration directive is option forwardfor
, which we see in your current setup. However, we need to enhance it with proper headers:
option forwardfor header X-Forwarded-For
option forwardfor except 127.0.0.1
Here's an optimized version of your configuration with IP preservation:
global
maxconn 4096
pidfile /var/run/haproxy.pid
daemon
defaults
mode http
retries 3
option redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
frontend http-in
bind xxx.xxx.xxx.xxx:80
option forwardfor header X-Forwarded-For
http-request set-header X-Real-IP %[src]
default_backend servers
backend servers
balance roundrobin
cookie GALAXY insert
option httpclose
stats enable
stats auth username:userpass
server app1 xxx.xxx.xxx.xxx:80 maxconn 1 check
For Apache, you'll need to configure mod_remoteip:
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
RemoteIPInternalProxy xxx.xxx.xxx.xxx # HAProxy IP
Create a test PHP page with:
<?php
echo "Remote IP: " . $_SERVER['REMOTE_ADDR'] . "<br>";
echo "X-Forwarded-For: " . $_SERVER['HTTP_X_FORWARDED_FOR'] . "<br>";
echo "X-Real-IP: " . $_SERVER['HTTP_X_REAL_IP'] . "<br>";
?>
Always validate IP headers to prevent spoofing. In HAProxy, you can add:
acl valid_ip src -f /etc/haproxy/whitelist.lst
http-request deny if !valid_ip
For more robust IP preservation, consider PROXY protocol in HAProxy 1.5+:
server app1 xxx.xxx.xxx.xxx:80 send-proxy maxconn 1 check
Then configure your backend server to accept PROXY protocol connections.