# 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.
Introduction
This article covers troubleshooting steps and solutions for How to Fix Nginx Real IP Header Issues. The error typically occurs in production environments and can cause service disruptions if not addressed promptly.
Symptoms
Common error messages include:
curl -H "X-Forwarded-For: 203.0.113.50" http://localhost/testadd_header X-Detected-IP $remote_addr always;```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/ ```
Common Causes
- Configuration misconfiguration
- Missing or incorrect credentials
- Network connectivity issues
- Version compatibility problems
- Resource exhaustion or limits
- Permission or access denied
Step-by-Step Fix
- 1.Check logs for specific error messages
- 2.Verify configuration settings
- 3.Test network connectivity
- 4.Review recent changes
- 5.Apply corrective action
- 6.Verify the fix
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
- 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; } } ```
Verification
| 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).
Related Articles
- [Nginx troubleshooting: Fix Lambda Permission Denied - Complete ](fix-lambda-permission-denied)
- [Nginx web server troubleshooting: Fix Client Max Body Size Large Upload Nginx Issue ](client-max-body-size-large-upload-nginx)
- [Fix Apache 502 Proxy Error](fix-apache-502-proxy-error)
- [Fix Apache LogLevel Core Debug Configuration](fix-apache-loglevel-core-debug)
- [Fix Cloudflare 502 Bad Gateway Error](fix-cloudflare-502-bad-gateway)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "How to Fix Nginx Real IP Header Issues", "description": "Troubleshoot Nginx real IP header problems. Fix Cloudflare, AWS, and proxy IP extraction with ngx_http_realip_module.", "url": "https://www.fixwikihub.com/fix-nginx-real-ip-header", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-11-28T04:02:50.809Z", "dateModified": "2025-11-28T04:02:50.809Z" } </script>