Getting real client IP through Varnish
After installation Varnish as frontend caching server your Web server ( like Apache, Nginx etc.) and application (PHP, ASP, JSP etc.) will not see real client IP from which request is coming. Rather than they will see client IP as 127.0.0.1 if Varnish and Web server are installed in the same machine. If varnish is installed in separate host then that host IP will be seen as client IP. In my earlier post Optimization of Apache Webserver With Varnish Installation you can see how Varnish is installed as front end caching server whereas Apache is installed as back end Web server. Follow the steps for “Getting real client IP through Varnish”
- Open up the /etc/varnish/default.vcl file and change it as mentioned
# vi /etc/varnish/default.vcl
This file tells Varnish where to look for the webserver content. In the sub vcl_recv section you need to mention set req.http.X-Forwarded-For = client.ip;
The default configuration file already has all necessary configuration lines. You just need to uncomment those lines. The configuration file should look like this:# This is a basic VCL configuration file for varnish. See the vcl(7) # man page for details on VCL syntax and semantics. # # Default backend definition. Set this to point to your content # server. # backend default { .host = "127.0.0.1"; .port = "8080"; } # Below is a commented-out copy of the default VCL logic. If you # redefine any of these subroutines, the built-in logic will be # appended to your code. 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; } } if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { # /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } if (req.request != "GET" && req.request != "HEAD") { # /* We only deal with GET and HEAD by default */ return (pass); } if (req.http.Authorization || req.http.Cookie) { # /* Not cacheable by default */ return (pass); } return (lookup); # return (pipe); } # sub vcl_pipe { # # Note that only the first request to the backend will have # # X-Forwarded-For set. If you use X-Forwarded-For and want to # # have it set for all requests, make sure to have: set bereq.http.connection = "close"; # # here. It is not set by default as it might break some broken web # # applications, like IIS with NTLM authentication. return (pipe); }
- Restart Apache and Varnish to make the changes effective
# /etc/init.d/varnish restart
- Edit httpd.conf file to get client IP in Apache access log file
Just add the following lines inside your Virtualhost declaration:LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnish CustomLog /var/log/httpd/yourdomain_log varnish
So for a domain www.example.com virtual host configuration for Apache would be
<VirtualHost *:8080> ServerAdmin webmaster@dummy-host.example.com DocumentRoot /var/www/www.example.com ServerName www.example.com ErrorLog /var/log/httpd/example.com-error_log LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnish CustomLog /var/log/httpd/example.com_log varnish </VirtualHost>
- Change application coding to have real client IP
In PHP we use $_SERVER[‘REMOTE_ADDR’] to get client IP. Now, you have to use $_SERVER[“HTTP_X_FORWARDED_FOR”] to get remote client IP. You can check it by making a test.php in your website directory and browse it from any browser<?php echo $_SERVER["REMOTE_ADDR"]; echo $_SERVER["HTTP_X_FORWARDED_FOR"]; ?>
The first line should print 127.0.0.1 or other IP where Varnish is installed. The second line should print the real client IP. So, in you all pages you should replace $_SERVER[“REMOTE_ADDR”] by $_SERVER[“HTTP_X_FORWARDED_FOR”].
If you think it is hazard for you there is another trick to solve this. You just need to automatically prepend the following PHP script to all PHP files that are ran behind Varnish. Open a file editor to create /etc/httpd/conf.d/varnish_client_ip.php, then add the following:<?php if( isset( $_SERVER[ 'HTTP_X_FORWARDED_FOR' ] ) ) { $_SERVER[ 'REMOTE_ADDR' ] = $_SERVER[ 'HTTP_X_FORWARDED_FOR' ]; }
You then need to add the following lines inside your Virtualhost declaration of Apache:
<Directory "/var/www/www.example.com"> php_value auto_prepend_file "/etc/httpd/conf.d/varnish_client_ip.php" </Directory>
Here /var/www/www.example.com is the document root for your domain www.example.com. So for a domain www.example.com virtual host configuration for Apache would be
<VirtualHost *:8080> ServerAdmin webmaster@dummy-host.example.com DocumentRoot /var/www/www.example.com ServerName www.example.com ErrorLog /var/log/httpd/example.com-error_log LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnish CustomLog /var/log/httpd/example.com_log varnish <Directory "/var/www/www.example.com"> php_value auto_prepend_file "/etc/httpd/conf.d/varnish_client_ip.php" </Directory> </VirtualHost>
Now enjoy the faster web server and feed your application and Apache by Getting real client IP through Varnish.