How-To: Configure Nginx Logs to Show Real IP When Behind a Reverse Proxy

CONTENTS

  1. BACKGROUND
  2. DIRECTIONS
    1. Proxy Server Configuration
    2. Web Server Configuration
  3. Wrapping Up
  4. References

EDIT (2015-04-10):

Based on suggestion from a kind reader, I updated the configuration to follow the more standardized naming of X-Real-IP versus X-RealIP. This in turn also changed $http_x_realip to now be $http_x_real_ip.

BACKGROUND

Recently while reviewing web server logs, I noticed that the requesting (client’s) IP address was the same for every request in the log.  Very quickly I realized this was due to the fact that this web server was sitting behind a reverse proxy. This posed an issue for me because I would not be able to tell what the actual client IP address was, which in turn means I would not be able to block the IP addresses of anyone performing an attack on the web server.

As an immediate solution I enabled access and error logging on my reverse proxy for that specific web site.  Technically this method works, as I would just have to look at the logs on the reverse proxy to see the client’s IP address, but my curiosity wasn’t satisfied with this solution.  I wanted to know how I could get the web server’s logs to display the client’s IP address as well.  An added benefit of having logging working on both systems, is that if the web server were to be compromised in an attack, the attacker would have to delete the logs on the web server server and then compromise the reverse proxy server to delete its logs in order to completely cover their tracks.

If you are not familiar with reverse proxy servers, do a little bit of searching or check the Wikipedia entry.  Below is a (crude) diagram of what a reverse proxy environment might look like.

Reverse Proxy Diagram
Reverse Proxy Diagram

Without further ado, here is how to configure nginx logs to show real IP when behind a reverse proxy server.

DIRECTIONS

The assumed environment is that you are using nginx on both the reverse proxy server and on the web server.  I will be using the diagram above as my environment, focusing on www.mysite.com (web server 1).

Proxy Server Configuration

  1. Edit your www.mysite.com configuration file.  This could be located in /etc/nginx/conf.d/, /etc/nginx/vhosts.d/, /etc/nginx/sites-available/, or directly in /etc/nginx/nginx.conf.  The config might looks something like this, but the most important part, for our logging objective, is the line that says “proxy_set_header X-Real-IP $remote_addr;”.
    server {
      listen 80;
      server_name mysite.com www.mysite.com;
    
      access_log      /var/log/nginx/mysite.com.log; #This is logging on the proxy server
      error_log       /var/log/nginx/mysite.com.log; #This is logging on the proxy server
    
      location / {
        proxy_pass http://10.10.10.11;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr; #Important line for our logging objective
      }
    }
  2. Restart the nginx service

    $ sudo service nginx restart

Web Server Configuration

  1. Edit /etc/nginx/nginx.conf.  Anywhere in the http{} section, place the following (with or without the comments):
    ## This log format makes it so we can see real requester's IP address \
    ##    not just the reverse proxy server's IP address. Also note, that \
    ##    "specialLog" can be replaced with any name you would like to \
    ##    give to this log format.
    log_format specialLog '$remote_addr forwarded for $http_x_real_ip - $remote_user [$time_local]  '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent"';
  2. Edit your www.mysite.com configuration file on the web server, and include the following in your server{} section:
    ##Put this anywhere you see fit in the server{} section.  If you have a \
    ##  second server{} section for an https listener, then you will need to \
    ##  put this in there too.  The name 'specialLog' should be changed to \
    ##  match the name of the log format you created above.
    access_log /path/to/you/log/directory/your-log-file-name.log specialLog;
  3. Restart the nginx service

    $ sudo service nginx restart

Wrapping Up

Now you should be able to see every log line, on your web server’s logs, start with “<ip.of.proxy.server> forwarded for <ip.of.client.machine>”.  The reason this works is two fold; one, with each request we forward from the reverse proxy to a web server, we are adding the HTTP header “X-Real-IP” with its value being the IP address of the requesting client machine; and two, in the web server’s log format we are including the HTTP header value “$http_x_real_ip”.  If you find that this is not working for you, verify the configuration files are correct and that you restarted the nginx service.  You can also run the command “nginx -V” and look for “–with-http_realip_module” in under the “configure arguments” section.

References

  • http://wiki.nginx.org/FullExample
  • http://wiki.nginx.org/NginxHttpLogModule#log_format_combined
  • http://serverfault.com/questions/486334/nginx-with-real-ip-log-both-client-remote-addr-and-proxies-address