Nginx as a reverse proxy for Apache

While Apache is a great server for delivering dynamic content and especially hosting PHP-based websites, it has a high memory footprint and a high overhead when forking new worker processes during high server load. In this article I will describe how you can use the nginx web server as a reverse proxy for your Apache to deliver static files instead of Apache. Nginx has a very small memory footprint and can deliver static files lightning fast.

The idea behind this setup is that nginx will listen on port 80 for incoming connections, identify whether the client requests a static file or a dynamic webpage. In case of a static file it will deliver the file itself. In case of a dynamic request it will forward that request to the Apache server.

So let’s get started. First we need to download and unzip the lates stabel version of nginx. Currently this is verions 0.6.32. Compilation and installation is a done with the usual steps:

./configure
make
make install

This will install nginx in the directory /usr/local/nginx. I usually like to have all my configuration files under /etc, so let’s copy the configuration folder over:

cp -r /usr/local/nginx/conf /etc/nginx

The sample configuration file nginx.conf is well suited as a starting point. I would recommend to uncomment / change the following settings in the main section:

user www-data www-data;
worker_processes 2;
pid /var/run/nginx.pid;

In the http-section you could alter the following settings:

tcp_noauth on;
gzip on;

The English nginx wiki contains a very good documentation on these settings.

Nginx has full support for name based virtual hosts and you need to create a server-section in the config for every virtual host that is configured in Apache. But first create a new configuration file /etc/nginx/proxy.conf which contains the basic proxy settings as found in the nginx wiki:

proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;

These settings will be reused in every virtual host. The default virtual host is used as fallback in case no specific configuration for that virtual host can be found. To configure this default host, replace the server-section in the file /etc/nginx.conf with the following block:

server {
    listen       XXX.XXX.XXX.XXX:80 default;
    server_name  _;
    access_log /var/log/nginx/default.access.log main;

    location / {
        proxy_pass http://127.0.0.1:80;
        include /etc/nginx/proxy.conf;
   }
}

Replace XXX.XXX.XXX.XXX with the extern IP address of the server. The above configuration is a pure proxy configuration which will pass all the traffic to the Apache server that is listening on 127.0.0.1:80.

To be flexible in the virtual host configuration, I like to maintain one configuration file per virtual host in a separate directory. We can include all configuration files from a certain directory into the nginx configuration by adding the following line after the server-section:

include /etc/nginx/sites-enabled/*;

So every time you want to setup a new virtual host, you just need to add a new configuration file to the directory /etc/nginx/sites-enabled. Here is a template for that file:

server {
    listen XXX.XXX.XXX.XXX:80;
    server_name foobar.com www.foobar.com;

    location / {
        proxy_pass http://127.0.0.1:80;
        include /etc/nginx/proxy.conf;
    }

    location ~* ^.+\.(jpe?g|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf|avi|mp3)$ {
        expires 30d;
        root /var/www/foobar.com/htdocs;
    }
}

This configuration will setup the virtual host foobar.com. You can define all alias addresses with the configuration directive server_name. All requests that match one of the above file extensions will be delivered directly by nginx from the directory /var/www/foobar.com/htdocs. All other requests are forwarded to the Apache server.

Before nginx can be started we need to make sure that Apache only listens on the address 127.0.0.1 for requests and not on the external IP address. In Debian this is done in /etc/apache2/ports.conf. Change this file to:

Listen 127.0.0.1:80

Also make sure, that the VirtualHost directives in the Apache configuration files do not include an IP address. They should look like this:

<VirtualHost *:80>
    ServerName foobar.com
...
</VirtualHost>

The directive NameVirtualHost should look like this (also without an IP address):

NameVirtualHost *:80

Now you can restart Apache:

/etc/init.d/apache2 restart

And start nginx:

/usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf

You can reload the nginx configuration without stopping nginx:

kill -HUP `cat /var/run/nginx.pid`

On my server some quick benchmarks have shown that nginx can deliver static content up to 10 times faster than Apache. The amazing thing is that not only did it deliver the content faster, there was nearly no impact on CPU or memory. With combining Apache and nginx we can have the best of both worlds, nginx for static files and Apache for dynamic content.

Leave a Reply