Showing the correct client IP in logs and scripts when using Nginx behind a reverse proxy

I’ve noticed when it comes to use of reverse proxies such as HAProxy to serve high availability websites that many people seem to struggle to get the real client IP address both in their server logs and scripting languages (e.g. PHP).

This is actually really easy to do, but it seems that the methods are not well known as there are numerous posts on forums and many people resort in attempting to resolve the problem by making code changes in their scripts and using custom logging settings on their web server.

With Nginx it’s EASY! I’m sure you can do it with other web servers too, but Nginx is my web server of choice these days due to it’s low memory footprint, speed and features.

For the sake of an example I’ll explain how this works in the context of the hosting that this website is running on. This is a pair of HAProxy servers in front of a pair of Nginx servers with PHP configured via FPM.

Firstly you need to configure HAProxy to pass the client IP address, which is quite simply done by adding the “forwardfor” and “http-server-close” options to the backend, ensuring that the real client IP reaches the backend web servers via the X-Forwarded-For header. Here is the configuration I’m using for my HTTP backend:

backend http
    mode http
    option httpchk GET /index.html
    balance leastconn
    option http-server-close
    option forwardfor
    server web1 <web1_ip>:80 check inter 1000 fastinter 500 downinter 5000 rise 3 fall 1
    server web2 <web2_ip>:80 check inter 1000 fastinter 500 downinter 5000 rise 3 fall 1

Secondly you need to configure Nginx to use the correct source for the client IP address using the HttpRealIP module.

There are 2 main directives you should define:

  • set_real_ip_from specifies the address of a trusted server (e.g. the load balancer) of which Nginx can replace this address with an untrusted address (the client IP)
  • real_ip_header specifies the source header to find the address to replace the trusted server with

These are also global directives, so can be placed in the main config in the ‘http’ section so that it takes effect for all virtual hosts.

Here is an example for a configuration using 2 load balancers:

http {
    set_real_ip_from <load_balancer1_ip>;
    set_real_ip_from <load_balancer2_ip>;
    real_ip_header X-Forwarded-For;

After making these simple changes (5 lines of configuration in total for a dual load balanced cluster) all http logs will display the correct client IP as will scripting languages see the correct IP in the REMOTE_ADDR header without requiring a single code change.