If you’re familiar with nginx, you might know that there is a module – the Real IP module, which will set the
$binary_remote_addr variables from a specific header. This means that you can get the client IP address in nginx even when you’re behind a proxy (CDN). You will have correct IP addresses in your access log as well as getting correct results when using Geo-IP module, etc. The real client IP addresses can also be very useful for statistics, tracing or for blocking or throttling requests from specific IP addresses.
The only thing you have to do to use the Real IP module is to specify which header that holds the client IP address and which sources are trusted. This is done with the
set_real_ip_from directives like in the following example.
# Look for client IP address in the X-Client-IP header real_ip_header X-Client-IP; # Set all sources as trusted. # The real IP module will only be used when the remote IP address is among the trusted. set_real_ip_from 0.0.0.0/0;
This far everything sounds great – there is a module that will do exactly what we want. So what is this blog post all about? Well, when using CloudFront, according to the CloudFront documentation the client IP address will be found as the first element in the header
X-Forwarded-For. This is also the standard way of using the
X-Forwarded-For according to Wikipedia. The nginx documentation for Real-IP module does, however, say that In case of X-Forwarded-For, this module uses the last ip in the X-Forwarded-For header for replacement.
So, the Real IP module will not work when using only the directives from the example above, as it will use a proxy IP address, rather than using the client IP address. To solve this we’ll need to take a look at the third and last of the module directives –
real_ip_recursive, which will tell nginx to not accept any trusted IP addresses as the client IP address when set to
on (not default). That is, nginx will reject all trusted IP addresses, specified by the
set_real_ip_from directive, from the
Now we just have to figure out which proxy IP addresses and subnets to be trusted. First we have the load-balancer address. The ELB IP address must be in the VPC subnet, and the client IP address will obviously not be, so start with adding the complete VPC subnet. Then we need all CloudFront IP addresses, which are found on the support forum, linked from the CloudFront documentation.
The resulting nginx configuration should look something like:
# Look for client IP in the X-Forwarded-For header real_ip_header X-Forwarded-For; # Ignore trusted IPs real_ip_recursive on; # Set VPC subnet as trusted set_real_ip_from 10.0.0.0/16; # Set CloudFront subnets as trusted set_real_ip_from 188.8.131.52/16; set_real_ip_from 184.108.40.206/16; set_real_ip_from 220.127.116.11/18; set_real_ip_from 18.104.22.168/19; set_real_ip_from 22.214.171.124/18; set_real_ip_from 126.96.36.199/22; set_real_ip_from 188.8.131.52/22; set_real_ip_from 184.108.40.206/23; set_real_ip_from 220.127.116.11/20; set_real_ip_from 18.104.22.168/19; set_real_ip_from 22.214.171.124/24; set_real_ip_from 126.96.36.199/23; set_real_ip_from 188.8.131.52/23; set_real_ip_from 184.108.40.206/24; set_real_ip_from 220.127.116.11/19;
Reload nginx and check the access log. Hopefully you will now find your IP address when making requests via CloudFront, as well as directly to your load balancer.