Safeguard
Security Guides•

nginx real_ip: trust X-Forwarded-For only from your own proxies

How set_real_ip_from, real_ip_header and real_ip_recursive pick the client address nginx logs and limits, and how broad trust lets users spoof it.

Safeguard Security Team
4 min read

Behind a load balancer, nginx sees the balancer's address, not the visitor's. The realip module fixes that by reading a header, but only for senders you list with set_real_ip_from. List too much and any client can claim any address, which defeats logging, allow lists and rate limits.

The problem it solves

If a load balancer sits in front of nginx, every connection comes from the balancer. Access logs show one address. A limit_req zone keyed on the client address treats all visitors as one user and blocks everyone together.

The load balancer usually appends the real address to the X-Forwarded-For header. nginx can use that, but only if you tell it whom to believe.

The three directives

set_real_ip_from 192.168.1.0/24;
set_real_ip_from 192.168.2.1;
real_ip_header   X-Forwarded-For;
real_ip_recursive on;

set_real_ip_from defines trusted sources. The module rewrites the client address only when the connection comes from one of them. It takes an address, a CIDR range, or unix:, and multiple lines accumulate.

real_ip_header names where the address comes from. The default is X-Real-IP. It can also be X-Forwarded-For or proxy_protocol, and the PROXY protocol option must also be set on the listen directive.

real_ip_recursive picks the entry when the header holds a chain. Off, the last address in the header wins. On, nginx walks from the right, skips trusted addresses, and uses the last untrusted one.

A worked example

A request arrives from trusted proxy 192.168.1.11 with X-Forwarded-For: 203.0.113.5, 192.168.1.10. With recursive off, the client becomes 192.168.1.10, which is another proxy. With recursive on, nginx skips 192.168.1.10 because it is trusted and picks 203.0.113.5.

That is why recursive mode fits chains of several proxies. With one trusted proxy that sets a single header, the defaults can be enough.

Where spoofing creeps in

Two mistakes recur.

First, trusting a whole network range. The docs warn in effect that trusting a broad range lets any client in that range spoof its address. Trust only your own balancers.

Second, trusting a proxy that does not strip or overwrite what clients send. If the first hop appends to a client supplied X-Forwarded-For, the left side of the chain is attacker controlled. Reading from the right, past your own proxies, is the safe direction. Read the leftmost value and you read whatever the client typed.

The original connection address stays available as $realip_remote_addr, so you can log both. Doing so makes spoofing attempts visible: a request whose logged client address differs wildly from the balancer path is worth a look.

Build and verify

The module is not built by default. It needs --with-http_realip_module, though many distribution packages include it. Check with nginx -V.

Test with curl from outside, sending your own header, then read your access log.

curl -s -o /dev/null -H "X-Forwarded-For: 1.2.3.4" https://example.com/

If the log shows 1.2.3.4 as the client for a request that did not come through your balancer, trust is too wide. See also the Forwarded header post for the standard version.

Where Safeguard fits

Safeguard does not manage proxy configuration. It focuses on the software behind it: dependency scanning, SBOMs and policy gates. You can try the free scan at https://gold.safeguard.sh/scan.

Quick check

What does real_ip_recursive on change?

nginx skips trusted addresses from the right of the header and uses the last untrusted one, instead of simply taking the last entry.

Is it safe to trust all of a cloud provider's address space?

No. Trust only the addresses of your own proxies or balancers, or any client in a broad range can spoof its address.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.