How to get the client IP when using CloudFront and nginx

If you’re familiar with nginx, you might know that there is a module – the Real IP module, which will set the $remote_addr and $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 real_ip_header and set_real_ip_from directives like in the following example.

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 X-Forwarded-For header.

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:

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.

This Post Has 6 Comments

  1. The networks in use by cloudflare is not a constant so it’s recommended to use and parse the official list (https://www.cloudflare.com/ips-v4). Point your monitoring tool of choice to so that you can get a notification when content change – that means it’s time to reflect the changes in (all) nginx (instances) configuration.

  2. Thank you so much, this was a life saver!

  3. “”ell, 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”

    I’m not sure if the documentation has changed since you wrote this or not, but the client IP is not always the first element of the x-forwarded-for header. If Cloudfront receives a request which already contains an x-forwarded-for header, it will append its REMOTE_ADDR to the end of this. ELBs and other systems may do the same, meaning the IP Cloudfront saw isn’t always the last IP in the list either.

    X-forwarded-for shouldn’t be used for identifying the client ip, it’s too unreliable. The correct way to do this is to configure the CDN to add their REMOTE_ADDR to a custom header, x-real-ip for example. The CDN should also nuke any existing value to this header on incoming requests, this way the header is guaranteed to contain one IP – the IP the CDN saw.

    Do you happen to know if Cloudfront can do this? They support custom headers, but the documentation doesn’t mention the ability to refer to Cloudfront variables like REMOTE_ADDR.

  4. Have you ever thought about publishing an e-book or
    guest authoring on other websites? I have a blog based upon on the same
    ideas you discuss and would really like to have you share some stories/information. I know my visitors would value your work.
    If you’re even remotely interested, feel free to shoot me an email.

  5. # Set VPC subnet as trusted
    set_real_ip_from 10.0.0.0/16

    Is this work for VPC EC2 instances , I want to make all my VPC EC2 server as trusted

Leave a Reply

Close Menu