You might have noticed by now that I really like Nginx. I’ve had an article for using it as a load balancer with SSL termination already, and eventually I’ll get around to setting it up as a web server as well. For this article, I’ll set a situation. You have some PHP application on apache, and have your mod_rewrite wizardry the way you want it, and everything is working -ok-. You’ve heard of this new Nginx thing and want to give it a shot, but don’t want to mess with testing everything on Nginx. So what you can do, is have Nginx listen and serve all your static content (Which it’s really good at), and pass your dynamic content(and whatever else) back to Apache to process. This article will go over the configuration of an Nginx reverse proxy, and modifying apache to work with it.

The first thing you need to do is install it. On Debian, this is quite simply –

Add the following to /etc/apt/sources.list

deb squeeze nginx
deb-src squeeze nginx

Now install it:

# aptitude update
# aptitude install nginx

Here’s some copy/pasta if you just want the configs:


user  nginx;
worker_processes  6;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/;

events {
    worker_connections  1024;

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  10;

    #Compression Settings
    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_min_length  1100;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    # Some version of IE 6 don't handle compression well on some mime-types, 
    # so just disable for them
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    # Set a vary header so downstream proxies don't send cached gzipped 
    # content to IE6
    gzip_vary on;

    include /etc/nginx/conf.d/*.conf;


The first bit is pretty self explanatory. We set the user, the number of processes nginx will spawn, where it’s going to store the pid file, and the number of connections each worker will get. Moving down, we have an include(mime.types sets a lot of the MIME types), and set the default MIME type. Moving further down, we set the log format and allow sendfile (which bypasses using read() and write(), you can read about it some here and here.) We set a keepalive_timeout fairly low (it could probably stand be shorter, but 10 seconds should be fine).

The compression settings should be fairly obvious, if you want to read on the specifics on a parameter you can read about them here.

Next, we need to set up the actual proxying:


server {

	listen 80;
	access_log off;
	error_log off;

	location / {
		proxy_redirect	off;	

		#Proxy Settings
		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;
		proxy_max_temp_file_size 0;
		proxy_connect_timeout      90;
		proxy_send_timeout         90;
		proxy_read_timeout         90;
		proxy_buffer_size          4k;
		proxy_buffers              4 32k;
		proxy_busy_buffers_size    64k;
		proxy_temp_file_write_size 64k;

	location ~* \.(?:ico|css|js|gif|jpe?g|png|bmp|html) {
		root /var/www;
		expires max;
		add_header Pragma public;
		add_header Cache-Control "public, must-revalidate, proxy-revalidate";

In this section, we do a few things. First we define a server. A server in the context of nginx can be loosely defined as a virtualhost. For instance, if you wanted to have and, you would end up with two server blocks. In this case, we’re going to specify a server with no server name. If you are going to host multiple domains, you will need to make a .conf for each and set server_name as well as root for each domain unless you don’t want nginx to serve static content for some reason.

The next thing we do is specify two locations. The first specifies anything that matches /, and anything that matches something ending in .ico/css/etc. Nginx matches the most specific location. In this instance, that means /somedir/image.png will match the second rule, as matching “.png” is more specific then matching / (which will match every request to the server). In the ~* \. match, we serve the content with nginx directly – this is because nginx is very good at serving static content and there is no reason to proxy the requests to apache. We add a couple headers (for instance, telling the browser to cache the content as long as possible) and let it go on its way. The other location proxies the request to apache, setting some headers (such as X-Real-IP and X-Forwarded-For), and set the timeouts to the backend (90 seconds) and the buffer sizes. I will refer you (again) to the excellent documentation if you would like to know what all parameters are available, located here.

Finally, we need to change apache to listen on port 8080 instead of 80. The apache configurations vary significantly between distributions, so I won’t point to the specific files, but a grep -Ri should get you what you need.

The configurations we are concerned about are VirtualHost, NameVirtualHost and Listen.

You will need to modify them so they look like this:

NameVirtualHost *:8080
Listen 8080

Once you have done this, check your configurations:

# service nginx configtest
# service apache configtest

(note that it is httpd on redhat based systems), assuming everything checks out, restart apache, start nginx and set it to start on boot –

# service apache2 restart
# service nginx start
# update-rc.d nginx enable

Assuming everything went well, Nginx should now be solving all static content and relaying everything else back to apache. Enjoy your performance boost!

« »