How To Speed Up Drupal 7.7 With Boost And nginx (Debian Squeeze) - Page 3

4 Adding nginx As A Reverse Proxy

It is possible to make your site even faster by adding nginx to the setup as a (not-caching) reverse proxy. We will make Apache run on port 8080 and nginx on port 80. Because nginx is so much faster in serving static files than Apache, we will configure it to directly check the cache directory if there is a cached copy for the request, i.e., we will bypass Apache. (We will tell nginx to do the same for other static content outside the cache directory such as images, Javascript, css as well.) If not, the request will be passed on to Apache. Requests from logged-in users plus POST requests will also be passed on to Apache and not be served from the cache.

First we install nginx and the mod-rpaf module for Apache (this module will make Apache logs show the actual IP from the visitor, not the IP of our nginx proxy - - i.e., it takes the last IP from the 'X-Forwarded-For' header):

apt-get install nginx libapache2-mod-rpaf

Next we configure Apache to listen on port 8080:

vi /etc/apache2/ports.conf
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default
# This is also true if you have upgraded from before 2.2.9-3 (i.e. from
# Debian etch). See /usr/share/doc/apache2.2-common/NEWS.Debian.gz and
# README.Debian.gz
NameVirtualHost *:8080
Listen 8080
<IfModule mod_ssl.c>
    # If you add NameVirtualHost *:443 here, you will also have to change
    # the VirtualHost statement in /etc/apache2/sites-available/default-ssl
    # to <VirtualHost *:443>
    # Server Name Indication for SSL named virtual hosts is currently not
    # supported by MSIE on Windows XP.
    Listen 443
<IfModule mod_gnutls.c>
    Listen 443

Change the port in all your vhosts as well, e.g.:

vi /etc/apache2/sites-available/
<VirtualHost *:8080>

Then restart Apache:

/etc/init.d/apache2 restart

As we will configure nginx to serve cached files directly (bypassing Apache), we can now remove or comment out the Boost rewrite rules in our .htaccess file:

vi /var/www/
##  ### BOOST START ###
##  # Allow for alt paths to be set via htaccess rules; allows for cached variants (future mobile support)
##  RewriteRule .* - [E=boostpath:normal]
##  # Caching for anonymous users
##  # Skip boost IF not get request OR uri has wrong dir OR cookie is set OR request came from this server OR https request
##  RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [OR]
##  RewriteCond %{REQUEST_URI} (^/(admin|cache|misc|modules|sites|system|openid|themes|node/add|comment/reply))|(/(edit|user|user/(login|password|register))$) [OR]
##  RewriteCond %{HTTPS} on [OR]
##  RewriteRule .* - [S=3]
##  # GZIP
##  RewriteCond %{HTTP:Accept-encoding} !gzip
##  RewriteRule .* - [S=1]
##  RewriteCond %{DOCUMENT_ROOT}/cache/%{ENV:boostpath}/%{HTTP_HOST}%{REQUEST_URI}_%{QUERY_STRING}\.html -s
##  RewriteRule .* cache/%{ENV:boostpath}/%{HTTP_HOST}%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html,E=no-gzip:1]
##  # NORMAL
##  RewriteCond %{DOCUMENT_ROOT}/cache/%{ENV:boostpath}/%{HTTP_HOST}%{REQUEST_URI}_%{QUERY_STRING}\.html -s
##  RewriteRule .* cache/%{ENV:boostpath}/%{HTTP_HOST}%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html]
##  ### BOOST END ###

Now we back up the original default vhost configuration of nginx and create a new one as follows:

cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default_orig
cat /dev/null > /etc/nginx/sites-available/default
vi /etc/nginx/sites-available/default
server {
        listen       80;
        # fill in the correct domain
        # fill in the correct document root
        root /var/www/;
        client_body_buffer_size 1m;
        proxy_buffering on;
        proxy_buffer_size   128k;
        proxy_buffers   4 256k;
        proxy_busy_buffers_size   256k;
        # Avoid image hotlinking from other sites and allow browsers to cache
        location ~* \.(js|css|jpg|jpeg|gif|png|svg|ico)$ {
                valid_referers blocked none ;
                if ($invalid_referer) {
                        return 403;
                if (-f $request_filename) {
                        expires      30d;
                        add_header Cache-Control public;
        # Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
        location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_ {
                deny all;
        # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
        location ~ /\. {
                deny all;
                access_log off;
                log_not_found off;
        # Pass on all PHP requests to the Apache backend
        location ~* \.php$ {
                proxy_pass http://localhost:8080;
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        location / {
                try_files $uri @cache;
        location @cache {
                if ($query_string ~ ".+") {
                        return 405;
				# pass requests from logged-in users to Apache
                if ($http_cookie ~ "DRUPAL_UID" ) {
                        return 405;
                } # pass POST requests to Apache
                if ($request_method !~ ^(GET|HEAD)$ ) {
                        return 405;
                error_page 405 = @drupal;
                # do not allow browsers to cache HTML
                add_header Expires "Sun, 19 Nov 1978 05:00:00 GMT";
                add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
                # serve requested content from the cache if available, otherwise pass the request to Apache
                try_files /cache/normal/$host/${uri}_.html /cache/perm/$host/${uri}_.css /cache/perm/$host/${uri}_.js /cache/$host/0$uri.html /cache/$host/0${uri}/index.html @drupal;
        # This sends the php to Apache, running on port 8080
        location @drupal {
                proxy_pass http://localhost:8080;
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        error_page 404 = index.php;
        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
                root   html;

I've added comments to the file so it should be clear what it does. One part you should take caution of is this part:

        # Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
        location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_ {
                deny all;

        # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
        location ~ /\. {
                deny all;
                access_log off;
                log_not_found off;

Because nginx bypasses Apache for static files, we must disallow nginx to serve files with certain extensions (such as configuration files containing passwords) - otherwise the visitor would see the source code in his browser. That's what we do with the above lines. If the visitor, for example, tries to load a file with the extension .inc (like, he will get a 403 Forbidden error now:

I've taken the list of forbidden files from Drupal's .htaccess file and added a few extensions.

Finally we edit /etc/nginx/nginx.conf...

vi /etc/nginx/nginx.conf

... and configure gzip compression (see How To Save Traffic With nginx's HttpGzipModule (Debian Squeeze)); you might also want to adjust the worker_processes, worker_connections, and keepalive_timeout settings (see for an explanation):

user www-data;
worker_processes  4;
error_log  /var/log/nginx/error.log;
pid        /var/run/;
events {
    worker_connections  1024;
    # multi_accept on;
http {
    include       /etc/nginx/mime.types;
    access_log  /var/log/nginx/access.log;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout   2;
    tcp_nodelay        on;
    # Enable gzip compression, see
    gzip  on;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
    gzip_buffers 16 8k;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

Finally we can start nginx:

/etc/init.d/nginx start

Now test again, and you should see that anonymous users get served cached pages by nginx. It should also feel a bit faster than before (when we only used Boost with Apache). If you use Apache Benchmark (ab) to test your site's performance, you should see that it is much faster than before thanks to nginx.



About The Author

Falko Timme is the owner of Boost Your Site mit Timme Hosting - ultra-schnelles nginx-WebhostingTimme Hosting (ultra-fast nginx web hosting). He is the lead maintainer of HowtoForge (since 2005) and one of the core developers of ISPConfig (since 2000). He has also contributed to the O'Reilly book "Linux System Administration".

Share this page:

2 Comment(s)

Add comment

Please register in our forum first to comment.


By: Tim Millwood

I would recommend Varnish rather than Boost.

By: KuN

It is worthy noting that in the config file of nginx

 location ~* \.(js|css|jpg|jpeg|gif|png|svg|ico)$ {
                valid_referers blocked none ;
                if ($invalid_referer) {
                        return 403;
                        break; can be replaced as * so that nginx would accept all subdomain requests.