Your App Logs Show Only the CDN IP, Not the Real Client
Behind a reverse proxy or CDN, your application sees connection from 10.0.0.1 or some Cloudflare IP instead of the actual user. This breaks rate limiting, geolocation, audit logs, and IP-based access control. The real client IP is in the headers — if you know which ones to trust.
Use OpsCheck HTTP Headers to see every header your server receives from the proxy layer. Then use OpsCheck IP Geolocation to verify the extracted client IP is a real public address, not an internal proxy IP.
Which Header Holds the Real IP
# Inspect all forwarded headers
curl -s -D - -o /dev/null https://example.com/ | grep -iE "forwarded|x-forwarded|cf-connecting|x-real"
# Common headers by provider:
# X-Forwarded-For: , ,
# X-Real-IP: (nginx)
# CF-Connecting-IP: (Cloudflare)
# True-Client-IP: (Akamai, some WAFs)
Trust Issues with X-Forwarded-For
X-Forwarded-For can contain multiple IPs — each proxy in the chain appends to it. The leftmost IP is supposed to be the client, but a malicious client can set this header before the first trusted proxy sees the request. If you blindly trust the leftmost entry, an attacker can spoof any IP.
# The chain looks like:
# X-Forwarded-For: SPOOFED_IP, real-client-ip, trusted-proxy-ip
# Correct approach: trust only the rightmost IP before your known proxy
# Or configure your web server to use the realip module
# nginx example:
# set_real_ip_from 10.0.0.0/8;
# real_ip_header X-Forwarded-For;
# real_ip_recursive on;
Cache Status Headers
# CDN cache status tells you which layer served the response:
# X-Cache: Hit from CloudFront
# CF-Cache-Status: HIT
# X-Cache: MISS, X-Cache-Lookup: HIT from backend
# Age: 3600 means this was cached 1 hour ago
# Verify with timestamp conversion if needed
Real-World Scenario
A webhook receiver was rate-limiting by IP. After moving behind a load balancer, every webhook appeared to come from 10.0.1.5 — the load balancer's internal IP. The rate limiter collapsed, treating all webhooks as a single source. The fix: configuring the application to read X-Forwarded-For and extracting the second-to-last entry (the one before the trusted load balancer). After the fix, rate limiting resumed correctly. The OpsCheck User Agent Parser confirmed the webhook sources were sending consistent User-Agent strings matching the restored IP diversity.
Debugging Checklist
- Log all received headers once to understand your proxy chain
- Identify which header your specific proxy/CDN uses for client IP
- Configure trusted proxy ranges — never trust X-Forwarded-For from any IP
- Verify the extracted IP is a routable public address, not RFC 1918
- Check cache status headers to understand which layer served the response