# Nginx Real IP Header Issues
Your application receives all requests from the same IP address - the load balancer, CDN, or proxy IP. User-specific features fail, rate limiting is ineffective, and logs show no real client diversity. The X-Forwarded-For header exists, but Nginx isn't using it. Real IP detection requires the ngx_http_realip_module and proper configuration.
Understanding Real IP Detection
- 1.When requests pass through proxies:
- 2.Client connects to proxy with real IP
- 3.Proxy connects to Nginx with proxy IP
- 4.Proxy adds
X-Forwarded-Forheader with real IP
- 1.Nginx must:
- 2.Trust the proxy (set_real_ip_from)
- 3.Know which header contains the real IP (real_ip_header)
- 4.Replace
$remote_addrwith the real client IP
Check current detected IP:
``bash
curl -H "X-Forwarded-For: 203.0.113.50" http://localhost/test
Add diagnostic header:
``nginx
add_header X-Detected-IP $remote_addr always;
Common Cause 1: Real IP Module Not Loaded
The module must be compiled or loaded.
Diagnosis: ```bash # Check if module is compiled nginx -V 2>&1 | grep real_ip
# Check for dynamic module ls /etc/nginx/modules/ | grep realip
# Check configuration grep -r "set_real_ip_from" /etc/nginx/ ```
Solution: Load module if dynamic:
``nginx
# In nginx.conf main context
load_module modules/ngx_http_realip_module.so;
Or install module: ```bash # Ubuntu/Debian - usually included in nginx-full sudo apt install nginx-full
# CentOS/RHEL - usually included by default sudo yum install nginx ```
Verify module:
``bash
sudo nginx -t
# Should not error on set_real_ip_from directive
Common Cause 2: Proxy IP Not Trusted
Only trusted proxies should provide real IPs.
Problematic config:
``nginx
real_ip_header X-Forwarded-For;
# No set_real_ip_from - any client can spoof their IP
Solution: Define trusted proxies: ```nginx # Cloudflare IPs set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 108.162.192.0/18; set_real_ip_from 131.0.72.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 162.158.0.0/15; set_real_ip_from 172.64.0.0/13; set_real_ip_from 173.245.48.0/20; set_real_ip_from 188.114.96.0/15; set_real_ip_from 190.93.240.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.0.0/17; set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32;
# AWS ALB set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12;
real_ip_header CF-Connecting-IP; # For Cloudflare # or real_ip_header X-Forwarded-For; # For generic proxies
real_ip_recursive on; ```
Common Cause 3: Wrong Header Used
Different proxies use different headers.
Cloudflare: CF-Connecting-IP
AWS ALB: X-Forwarded-For
Generic proxy: X-Forwarded-For or X-Real-IP
Problematic config:
``nginx
set_real_ip_from 103.21.244.0/22; # Cloudflare
real_ip_header X-Forwarded-For;
# Cloudflare sends CF-Connecting-IP, X-Forwarded-For might have multiple IPs
Solution: Use correct header: ```nginx # For Cloudflare set_real_ip_from 103.21.244.0/22; real_ip_header CF-Connecting-IP;
# For AWS ALB set_real_ip_from 10.0.0.0/8; real_ip_header X-Forwarded-For;
# For custom proxy set_real_ip_from 192.168.1.100; real_ip_header X-Real-IP; ```
Common Cause 4: X-Forwarded-For Chain Issues
X-Forwarded-For contains a chain of IPs: client, proxy1, proxy2, etc.
Header format:
``
X-Forwarded-For: 203.0.113.50, 10.0.0.1, 172.16.0.2
Problem: Nginx takes the first IP without real_ip_recursive:
``nginx
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
# $remote_addr becomes 172.16.0.2 (last IP, closest to Nginx)
Solution: Enable recursive:
``nginx
set_real_ip_from 10.0.0.0/8; # Trust first proxy
set_real_ip_from 172.16.0.0/12; # Trust second proxy
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Now Nginx walks through chain, stops at first untrusted IP
# Result: $remote_addr = 203.0.113.50 (real client)
Common Cause 5: Multiple Proxies Not All Trusted
Chain includes untrusted proxies, stopping resolution too early.
Problem: ```nginx set_real_ip_from 172.16.0.0/12; real_ip_header X-Forwarded-For; real_ip_recursive on;
# X-Forwarded-For: 203.0.113.50, 10.0.0.1, 172.16.0.2 # 172.16.0.2 is trusted, 10.0.0.1 is NOT trusted # Result: $remote_addr = 10.0.0.1 (intermediate proxy) ```
Solution: Trust all proxies in chain:
``nginx
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
Common Cause 6: Header Spoofing Not Prevented
Without trusted proxy list, clients can spoof IPs.
Problematic config:
``nginx
real_ip_header X-Forwarded-For;
# No set_real_ip_from
# Client can send: X-Forwarded-For: 8.8.8.8
# Nginx believes client is 8.8.8.8
Solution: Always set trusted proxies:
``nginx
# Only trust your actual proxies
set_real_ip_from 192.168.1.100; # Your load balancer
set_real_ip_from 10.0.0.50; # Your CDN origin
real_ip_header X-Forwarded-For;
Test spoofing protection: ```bash # Direct request (not through trusted proxy) curl -H "X-Forwarded-For: 8.8.8.8" http://your-server/test # Should NOT show 8.8.8.8 as remote_addr
# Request through trusted proxy curl -H "X-Forwarded-For: 203.0.113.50" --proxy 192.168.1.100 http://your-server/test # Should show 203.0.113.50 as remote_addr ```
Common Cause 7: Application Not Using $remote_addr
Nginx corrects the IP, but application reads wrong header.
Problem: ```nginx set_real_ip_from 10.0.0.0/8; real_ip_header X-Forwarded-For;
# Application reads X-Forwarded-For directly, not $remote_addr # Application might get wrong IP from chain ```
Solution: Forward corrected IP: ```nginx location / { set_real_ip_from 10.0.0.0/8; real_ip_header X-Forwarded-For;
proxy_pass http://backend; # Nginx's $remote_addr now contains real IP proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } ```
$proxy_add_x_forwarded_for:
- Starts with $remote_addr (the corrected real IP)
- Appends original X-Forwarded-For if present
- Backend receives clean chain starting with real client
Common Cause 8: Cloudflare IPv6 Not Trusted
IPv6 Cloudflare IPs missing from trusted list.
Problem:
``nginx
set_real_ip_from 103.21.244.0/22; # IPv4 only
real_ip_header CF-Connecting-IP;
# IPv6 clients still show Cloudflare IPv6 IP
Solution: Include IPv6 ranges: ```nginx # IPv4 set_real_ip_from 103.21.244.0/22; set_real_ip_from 173.245.48.0/20; # ... other IPv4 ranges
# IPv6 set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP; ```
Common Cause 9: Different Headers per Location
Need different settings for different paths.
Solution: Set per location: ```nginx server { location /api { set_real_ip_from 10.0.0.0/8; real_ip_header X-Forwarded-For; proxy_pass http://backend; }
location /cdn-content { set_real_ip_from 173.245.48.0/20; # Cloudflare real_ip_header CF-Connecting-IP; root /var/www/html; } } ```
Verification Steps
- 1.Add diagnostic headers:
- 2.```nginx
- 3.add_header X-Original-IP $remote_addr always;
- 4.add_header X-Forwarded-For-Original $http_x_forwarded_for always;
- 5.
` - 6.Test with curl:
- 7.```bash
- 8.# Test direct
- 9.curl -I http://your-server/test
# Test with spoofed header (should be ignored) curl -H "X-Forwarded-For: 8.8.8.8" -I http://your-server/test
# Test through proxy simulation curl -H "X-Forwarded-For: 203.0.113.50" -H "CF-Connecting-IP: 203.0.113.50" -I http://your-server/test ```
- 1.Check logs:
- 2.```nginx
- 3.log_format realip '$remote_addr - $http_x_forwarded_for - $http_cf_connecting_ip [$time_local]';
- 4.access_log /var/log/nginx/realip.log realip;
- 5.
`
tail -f /var/log/nginx/realip.log- 1.Verify application receives correct IP:
- 2.```bash
- 3.curl http://your-server/api/whoami
- 4.# Should return your real IP, not proxy IP
- 5.
`
Complete Working Configuration
```nginx # Cloudflare configuration set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 108.162.192.0/18; set_real_ip_from 131.0.72.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 162.158.0.0/15; set_real_ip_from 172.64.0.0/13; set_real_ip_from 173.245.48.0/20; set_real_ip_from 188.114.96.0/15; set_real_ip_from 190.93.240.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.0.0/17;
# IPv6 Cloudflare set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32;
real_ip_header CF-Connecting-IP; real_ip_recursive on;
server { listen 80; server_name example.com;
# Diagnostic headers (remove in production) add_header X-Real-IP $remote_addr always;
location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ```
Quick Reference
| Issue | Cause | Fix |
|---|---|---|
| All requests same IP | Module not loaded | Load realip module |
| All requests same IP | Proxy not trusted | Add set_real_ip_from |
| Wrong IP detected | Wrong header | Use correct header for proxy |
| Chain IP issue | No recursive | Enable real_ip_recursive |
| Spoofed IPs accepted | No trusted list | Always set trusted proxies |
| IPv6 not detected | IPv6 CIDR missing | Add IPv6 proxy ranges |
| App sees wrong IP | App reads wrong header | Forward $remote_addr to backend |
Real IP detection requires trusting specific proxies and using the correct header. Without proper configuration, either IPs are wrong (showing proxy IP) or spoofable (accepting any header value).