# 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. 1.When requests pass through proxies:
  2. 2.Client connects to proxy with real IP
  3. 3.Proxy connects to Nginx with proxy IP
  4. 4.Proxy adds X-Forwarded-For header with real IP
  1. 1.Nginx must:
  2. 2.Trust the proxy (set_real_ip_from)
  3. 3.Know which header contains the real IP (real_ip_header)
  4. 4.Replace $remote_addr with 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. 1.Add diagnostic headers:
  2. 2.```nginx
  3. 3.add_header X-Original-IP $remote_addr always;
  4. 4.add_header X-Forwarded-For-Original $http_x_forwarded_for always;
  5. 5.`
  6. 6.Test with curl:
  7. 7.```bash
  8. 8.# Test direct
  9. 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. 1.Check logs:
  2. 2.```nginx
  3. 3.log_format realip '$remote_addr - $http_x_forwarded_for - $http_cf_connecting_ip [$time_local]';
  4. 4.access_log /var/log/nginx/realip.log realip;
  5. 5.`
bash
tail -f /var/log/nginx/realip.log
  1. 1.Verify application receives correct IP:
  2. 2.```bash
  3. 3.curl http://your-server/api/whoami
  4. 4.# Should return your real IP, not proxy IP
  5. 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

IssueCauseFix
All requests same IPModule not loadedLoad realip module
All requests same IPProxy not trustedAdd set_real_ip_from
Wrong IP detectedWrong headerUse correct header for proxy
Chain IP issueNo recursiveEnable real_ip_recursive
Spoofed IPs acceptedNo trusted listAlways set trusted proxies
IPv6 not detectedIPv6 CIDR missingAdd IPv6 proxy ranges
App sees wrong IPApp reads wrong headerForward $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).