Should we trust the 'real' ip?

What really is Real IP address ? This comes into picture when a client makes a request to a remote server and the request is served through multiple reverse proxies. The IP address of the client from which the request is originating is the real IP address.

In enterprise software application, knowing the real IP of the client becomes essential in following scenarios:

  1. Rate limiting based on IP address
  2. Restricting access to an API for specific IP address

There are three configurations to resolve the original client IP address:

  1. real_ip_header: The header name whose value is considered as the IP address (Nginx documentation best explains this)
  2. set_real_ip_from: The values in this configuration contains IP addresses that NGINX trusts (believes, is sending the right IP address).
  3. real_ip_recursive: Nginx searches for that client IP address which is sent by the last trusted IP address.

Lets try to understand these configurations with the following scenarios:

We have a client (IP: 90.0.0.1) sending a request to an upstream service and consider that the request passes through two proxies. Proxy 1 (IP: 85.0.0.1) which reverse proxies to another server Proxy 2 (IP: 85.0.0.2)

Trusted IPs not set in both proxies

real_ip_header: X-Forwarded-For

If set_real_ip_from is not defined in Proxy 1 and Proxy 2, then simply Proxy 1 believes that client IP is actually the source IP address. Proxy 2 believes that client IP is the Proxy 1's IP.

Proxy 1 sees:

x-forwarded-for: 90.0.0.1
# hence resolves the original client ip address as 90.0.0.1, appends its own IP address to the header

Proxy 2 sees:

x-forwarded-for: 90.0.0.1, 85.0.0.1
# hence resolves the original client ip address as 85.0.0.2

Trusted IPs set but recursive search disabled in both proxies

Given proxy 2 trusts proxy 1 Proxy 1 sees:

x-forwarded-for: 90.0.0.1
# hence resolves the original client ip address as 90.0.0.1, appends its own IP address to the header

Proxy 2 sees:

x-forwarded-for: 90.0.0.1, 85.0.0.1
# since proxy 2 trusts proxy 1 but recursive search is disabled, proxy 2 resolves the client ip address as the last trusted IP address in the list which is 85.0.0.1

Although we have a trusted IP here, the original IP address of the client is lost midway through the upstream service. To combat this, lets consider the third scenario.

Trusted IPs set and recursive search enabled in both proxies

Proxy 1 sees:

x-forwarded-for: 90.0.0.1
# hence resolves the original client ip address as 90.0.0.1, appends its own IP address to the header

Proxy 2 sees:

x-forwarded-for: 90.0.0.1, 85.0.0.1
# since proxy 2 trusts proxy 1 and recursive search is enabled, proxy 2 resolves the client ip address as the last untrusted IP address in the list which is 90.0.0.1

Although this approach is sufficient for the most part, it’s still possible for the bad guys to spoof IPs and create memory exhaustion attacks.

Further reading

The perils of “real” client IP

How AWS ELB computes XFF

Parsing multiple XFF headers in a request

Raghavi Kannan avatar
Raghavi Kannan
I'm a software developer with over 7 years of experience. System design and architecture, infrstructure and networking are somethings that fascinates me.