Nginx PHP REMOTE_ADDR with Proxy (Varnish Cache)

Typically your public web server with consist of two principle pieces of software, the web server such as Nginx (Apache is more commonly used) and some server side scripting technology such as php (in my case php-fpm).

However many people are now either installing cache engines (such as Varnish) or load balancers infront of their web server to increase performance and possibly availability. Which can play havoc for server side scripts that use the client information.

Your HTTP requests are now handled by the cache (Varnish) so PHP variables such as $_SERVER[‘REMOTE_ADDR’] will no longer hold the client IP, however they will have your varnish cache IP, 127.0.0.1 if it’s on the same physical server.

This poses a problem because many scripts now use the client IP address, this could be for Spam protection, session tracking/security, or just statistics logging.

Some may say you should use another variable in your script such as $_SERVER[‘X-Forwarded-For’], this way your script can know the client IP and also the proxy IP, while the theory might have some benifits most people don’t write their own code, and chances are it’s configured to use REMOTE_ADDR.

So the fix is to have Varnish assign the real user’s IP address to a variable called “X-Forwarded-For”, this is done as below:

#Backup the configuration
cp /etc/varnish.default.vcl /etc/varnish.default.vcl.backup

#Open your varnish configuration
nano -w /etc/varnish.default.vcl

#Add the following lines to sub vcl_recv
sub vcl_recv {  
        remove req.http.X-Forwarded-For;
        set    req.http.X-Forwarded-For = req.http.rlnclientipaddr;
}

#Save and exit, then restart the varnish service
service varnish restart

So your cache engine is now passing the IP back to Nginx, now we just need configure Nginx to capture this variable and replace REMOTE_ADDR with it. To do this you need a Nginx module installed called http_realip_module, so run the following command and look out for “–with-http_realip_module”:

nginx -V
nginx version: nginx/0.7.67
TLS SNI support enabled
configure arguments: --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-debug --with-http_dav_module --with-http_flv_module --with-http_geoip_module --with-http_gzip_static_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-ipv6 --with-mail --with-mail_ssl_module --add-module=/tmp/buildd/nginx-0.7.67/modules/nginx-upstream-fair

As you can see I have it installed already, as you hopefully will do, now I just need to use the following configuration to perform the variable replacement.

#Create a backup of the configuration
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup

#Edit the configuration file
nano -w /etc/nginx/nginx.conf

#Add the following lines to your http{} statement
set_real_ip_from   127.0.0.1;
real_ip_header     X-Forwarded-For;

#Save, close and restart the nginx service
service nginx restart

Ok, just to explan what that configuration did, “set_real_ip_from” is going to be the IP address of your cache/load balancer basically the IP your currently seeing all of the proxied requests come from, in my case this was 127.0.0.1 as the cache and nginx are on the same server.

And “real_ip_header” defines what variable actually has the correct information for “REMOTE_ADDR” as you will see this matches what we setup Varnish to pass it back as.

You should be able to test if this works using the below test script.

echo $_SERVER["REMOTE_ADDR"] ;
echo "
".time()."
";
echo $_SERVER["HTTP_X_FORWARDED_FOR"] ;

Echoing the time just makes sure your not getting a varnish cache hit.

Hope that helps, please leave any questions you still have below…

Technology enthusiastic with many ongoing online projects one of which is this personal blog PingBin. While also working full time within a data center designing and maintaining the network infrastructure.

5 comments On Nginx PHP REMOTE_ADDR with Proxy (Varnish Cache)

  • Hello,

    Thanks for this tip !

    Varnish & nginx rock 😉

  • Hey Tom,

    Thank you very much for this! I now have proper IP addresses in my access logs (served by NGINX behind Varnish).

    Strangely, Bing Bot still shows as 127.0.0.1 on every visit, but apart from that, your fix works great! Would you have any thoughts about this, at all?

    Here’s an example of what I’m seeing…
    127.0.0.1 – – [11/Dec/2012:20:40:25 +0000] “GET /tag/tagname HTTP/1.1” 200 5733 “-” “Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)”

    Thanks again,
    Mark.

  • Thanks, works perfectly, perfect tutorial. +1

  • Thanks for that as it was quite helpful.
    For anyone that is using docker with seperate containers for nGinx and Varnish and the AWS ELB.

    Varnish
    ——-
    sub vcl_recv {
    if (req.restarts == 0) {
    if (req.http.x-forwarded-for ) {
    set req.http.X-Forwarded-For = req.http.X-Forwarded-For + “, ” + client.ip;
    }
    else {
    set req.http.X-Forwarded-For = client.ip;
    }
    }
    set req.http.x-server-address = std.ip( regsub(req.http.X-Forwarded-For, “^([^,]+),?.*$”, “\1”) , “0.0.0.0”);

    nginx.conf http { } block

    set_real_ip_from 0.0.0.0/0;
    real_ip_header x-server-address;

    You should be able to see the variable $_SERVER in PHP:
    [HTTP_X_SERVER_ADDRESS] =

Leave a reply:

Your email address will not be published.