NGINX Real Client IP Behind a Load Balancer Prompt
Recover the true client IP when NGINX sits behind an L7 load balancer or CDN — configure real_ip / X-Forwarded-For or PROXY protocol with trusted-proxy scoping — so rate limits, geo rules, and access logs stop keying on the load balancer's address.
- Target user
- Engineers running NGINX behind an ALB/ELB, GCLB, Cloudflare, or another proxy
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT, Cursor
The prompt
You are a senior NGINX engineer who fixes "everything looks like it comes from the load balancer" incidents. You understand the `ngx_http_realip_module`, the difference between `X-Forwarded-For` and PROXY protocol, and why trusting a client-supplied header without scoping it to trusted proxies is a spoofing vulnerability. I will provide: - What sits in front of NGINX: [ALB/ELB / GCLB / Cloudflare / another NGINX / HAProxy] - How the front layer forwards the client IP: [X-Forwarded-For header / PROXY protocol v1 or v2 / X-Real-IP] - The proxy/CDN source IP ranges (or that they are dynamic): [CIDRS OR "dynamic, published list"] - What is currently broken: [logs show LB IP / rate limiting hits everyone / geo rules wrong] - Whether TLS terminates at the LB or at NGINX: [DESCRIBE] Produce the fix: 1. **Choose the mechanism** — recommend header-based `real_ip` (`X-Forwarded-For`/`X-Real-IP`) vs PROXY protocol based on what the front layer actually sends, and explain the trade-off (PROXY protocol survives non-HTTP and can't be spoofed by clients; XFF is simpler but must be trusted carefully). 2. **Scope trust tightly** — configure `set_real_ip_from` for **only** the load balancer / CDN CIDRs, plus `real_ip_header` and `real_ip_recursive on;` where a chain of proxies is involved. Explain exactly why an unscoped `set_real_ip_from 0.0.0.0/0;` lets any client forge their IP and bypass rate limits and allow-lists. 3. **PROXY protocol path** — if used, show `listen ... proxy_protocol;` and `set_real_ip_from` for the LB, and warn that once `proxy_protocol` is enabled the port will reject any connection that does not speak it (so a health check hitting it directly will fail). 4. **Propagate downstream** — set `proxy_set_header X-Real-IP $remote_addr;` and `X-Forwarded-For $proxy_add_x_forwarded_for;` correctly for upstreams behind this NGINX, and fix the log format to record `$remote_addr` now that it reflects the real client. 5. **Verify** — give a concrete test (curl through the LB with a spoofed `X-Forwarded-For`) proving that a client outside the trusted CIDRs cannot override `$remote_addr`. Output: (a) the complete commented config (server/listen + real_ip directives + log_format), (b) a table of each directive and the spoofing or logging failure it addresses, (c) the verification commands, and (d) the `nginx -t` line. Validate with `nginx -t` and reload; do not enable `proxy_protocol` on a live listener without confirming the front layer sends it.
Run this prompt with AI
Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.
Why this prompt works
The symptom is always the same and always confusing: every request in the access log, every rate-limit bucket, and every geo decision suddenly keys on one or two IPs — the load balancer’s. The instinct is to trust X-Forwarded-For and move on, which quietly introduces a spoofing hole, because a header a client can set is a header a client can lie about. This prompt forces the one decision that makes the fix safe: which upstream addresses are allowed to assert a client IP.
By requiring set_real_ip_from to be scoped to the actual proxy CIDRs — and by explicitly naming the 0.0.0.0/0 anti-pattern — the prompt turns “recover the real IP” into “recover the real IP without letting anyone forge it.” That distinction is the entire security value; an unscoped real_ip config is arguably worse than none, because it launders spoofed headers into a trusted $remote_addr.
It also handles the two operational traps people hit the hard way: enabling PROXY protocol on a listener and then watching health checks and direct hits fail because they don’t speak it, and forgetting to update the log format so the “fix” appears not to work. The mandatory spoof-a-header verification step means the engineer ships something they’ve actually proven, not something that merely looks right.
Related prompts
-
NGINX CORS & Preflight Proxy Prompt
Get correct, secure CORS handling in NGINX for a reverse-proxied API — OPTIONS preflight, Access-Control-* headers, credentials, and per-origin allow-listing — without the classic duplicate-header and wildcard-with-credentials footguns.
-
NGINX GeoIP2 Country Access Control Prompt
Add country- or ASN-based access control and routing to NGINX with the ngx_http_geoip2 module — allow/deny by country, route regional traffic, and set headers — while correctly resolving the real client IP behind a CDN or load balancer.
-
NGINX Slowloris & Timeout Hardening Prompt
Harden NGINX against slow-client and connection-exhaustion attacks — client/header/body timeouts, connection and request-rate limits, and buffer caps — tuned so real slow mobile clients aren't collateral damage.
-
NGINX Stream (TCP/UDP) Load Balancing Prompt
Load-balance non-HTTP traffic through NGINX's stream module — TCP services like databases and message brokers, or UDP like DNS and syslog — with health checks, PROXY protocol, timeouts, and TLS passthrough or termination.
More NGINX prompts & error guides
Browse every NGINX prompt and troubleshooting guide in one place.
Reading prompts? Get all 500 in one free PDF
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.