(ve):Using Nginx as a Reverse Web Proxy

  • This page was last modified on February 21, 2011, at 10:43.
The (mt) Community Wiki is a collaborative project. Any (mt) Media Temple customer or employee may contribute. Not all articles and/or content have been tested for accuracy by (mt) Media Temple.

For officially moderated and tested articles, be sure to visit our KnowledgeBase.

From (mt) Community Wiki

Contents

Many websites using a traditional LAMP set up can dramatically decrease server load by employing a reverse proxy for static content. In this article, we will explain how to configure the Nginx web server to perform this function. The configuration steps shown here are applicable to Ubuntu and Debian servers, both of which are available options on the (ve) server platform.

What is a Reverse Proxy?

A reverse proxy here is a server that sits between the outside world and Apache, performs some function that Apache is currently doing, but can do it faster and with less strain on the server. In this case, that function is serving static content such as images, css, javascript, text, or xml files. Apache is an excellent piece of software with an enormous wealth of functionality, but it is also very large, and is essentially overkill when it comes to serving these static filetypes. Simply put, you don't need a big server process that has things like an embedded PHP (or Perl, or Python) interpreter inside it just to serve a small css file that doesn't need any interpretation. Instead, we will use Nginx, which is much faster and requires only a fraction of the system resources. This often results in enormous performance gains, as typically the significant majority of objects that need to be served to generate a web page are actually static files.

How Does it Work?

The idea is that we will chain Nginx and Apache together to work in tandem. When a request comes in to the server from the Internet, it will first be processed by Nginx. If Nginx determines that the request is for a static file, it handles the request itself. If it decides that the request is for anything other than a static file, it sends the request through to Apache so that it can be processed appropriately. By doing this, we achieve a setup that lets the different webservers each handle the jobs they are best at. Nginx handles serving the static files, and Apache is therefore only invoked for something like a PHP script that needs to be interpreted.

Installing the Needed Software

All of the software we need is available from the standard repositories. From the command prompt, as the root user, run the following commands to install Nginx, Apache, and the Apache mod_rpaf module.


apt-get update
apt-get -y install apache2 apache2-mpm-prefork libapache2-mod-rpaf nginx

This should install all the needed packages.

Configuring Nginx

Now, let's configure Nginx to serve static file types. We'll assume the following for the rest of this tutorial:

  • The domain name of your site is example.com
  • You want to forcibly remove any leading www in the site's URI (e.g. www.example.com rewrites automagically to example.com)
  • The public IP address of your site is 11.22.33.44
  • The document root where your site's files are located is /var/www/example.com

From the shell, go into the Nginx configuration directory where we define virtual hosts:


cd /etc/nginx/sites-available

Now, using your text editor of choice, create a file called example.com.conf with the following contents:



###
# comment out this first stanza if you DO NOT want the leading www. stripped from the URI.
server {
    listen 11.22.33.44:80;
    server_name www.example.com;
    rewrite ^ http://example.com$request_uri?;
}

server {
    listen 11.22.33.44:80;
    server_name example.com;
    server_name_in_redirect off;

    access_log /var/log/nginx/example.com-access.log;

    location / {
        proxy_pass http://127.0.0.1:80;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location ~ \.(jpg|jpeg|png|gif|swf|flv|mp4|mov|avi|wmv|m4v|mkv|ico|js|css|txt|html|htm)$ {
        root /var/www/example.com;
        access_log /var/log/nginx/example.com-access.log;

        gzip on;
        gzip_comp_level 2;
        gzip_vary on;
        gzip_proxied any;
        gzip_types text/plain text/xml text/css application/x-javascript;

        ###
        # uncomment the next line to enable expires headers
        # expires 7d;
    }
}

There are a few things in this configuration to pay attention to.

  1. The first stanza defined by the server { ... } notation is the part that strips off the leading www. from the domain name. Comment this out if you don't want that behavior.
  2. Inside the second server { ... } stanza, there are two nested location stanzas. The first, defined by location / { ... } is the part that proxies requests through to Apache. It does the proxy operation, and sets some headers that Apache will need when it gets these requests.
  3. The next location stanza, which begins with location ~ is the part that identifies which files are static and will be handled by Nginx. It turns compression on for the text based file types with the directives that start with gzip*. There's also a directive, commented out, that would turn on expires headers for these file types for one week. If you can do this, you should enable it by uncommenting that line.

The only step left to configure Nginx is to activate the configuration. We will disable the default, and enable our configuration with the following commands:



cd /etc/nginx/sites-enabled
rm -fv default
ln -s ../sites-available/example.com.conf 001-example.com.conf

You can verify that the configuration is valid by running the following command, and making sure the output indicates success like so:


nginx -t
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful

Configuring Apache

Now that we've set up Nginx to work as a proxy, let's configure Apache to receive the proxied requests that made it past Nginx. First, let's go into the Apache configuration directory:


cd /etc/apache2

You'll remember from the Nginx configuration section that we proxy requests through to port 80 listening on the localhost IP address which is 127.0.0.1. Therefore, we need to make sure Apache is only listening on that IP address. To accomplish this, we first edit the ports.conf file located in the /etc/apache2 directory. Open it up in your favorite text editor, and look for lines that say:


NameVirtualHost *:80
Listen 80

These tell Apache to listen to all IP addreses on the server on port 80, which is not what we want. We want to make sure it is only listening to the localhost IP since that is where the requests are going to go. Also, Nginx is already listening on the public IP address, so we can't bind to it on the same port from Apache. To get the desired behavior change those lines to the following:


NameVirtualHost 127.0.0.1:80
Listen 127.0.0.1:80

Next we must make sure that any virtual hosts we have defined are also tied to the local IP address. Go into the sites-available directory:


cd /etc/apache2/sites-available

and open up any files you have that define virtual hosts in your text editor. Assuming you are just using the default file, this file is simply called default. At the beginning of a virtual host definition you will see something like the following:


<VirtualHost *:80>

In a manner analagous to the ports.conf file, this must be changed to:


<VirtualHost 127.0.0.1:80>

The rest of your virtual host configuration should be able to remain unchanged.

As before with Nginx, you need to symlink any virtual host definitions you have made in the sites-available directory into the Apache sites-enabled directory in order for Apache to see their configuration. If you are just using the default file, this was done for you by the Apache installation process.

Logging and Proxied IPs

Because Apache is sitting "behind" Nginx, as far as it's concerned, all of the requests that reach it originate from Nginx itself. This has the adverse side effect that Apache will think the requesting IP address is always the machine's IP address, which is 127.0.0.1. There are a variety of problems this can cause. Many applications track the amount of activity from individual IPs to mitigate things like spam, and Apache records the requesting IP addresses by default in its log files.

Fortunately these problems are conveniently circumnavigated for us because we installed the Apache mod_rpaf module in a previous section. The "rpaf" part stands for "reverse proxy add forward", and it neatly fixes things. Using the extra header information we added to the proxied requests in the Nginx configuration, this module changes the internal Apache structures for things like the requested host name and the requesting IP address, so Apache will transparently pick up the correct values. The default configuration for mod_rpaf which was set up at install time is suitable for the environment we have configured between Nginx and Apache.

Finishing Up

All that's really needed now is to restart things so that they pick up their new configurations:


/etc/init.d/apache2 restart
/etc/init.d/nginx restart

Having done this, we have successfully configured Nginx to act as a reverse proxy as desired. It will now cherry pick requests for static file types, and send other kinds of requests through to Apache for processing. The setup has two very convenient features.

  1. It will often use substantially fewer machine resources compared to just using Apache.
  2. It should require no changes at all to your site's application code. Everything that was working should still just work.

You may wish to read more about the available configuration options. The documentation for Apache and Nginx is located at:

Enjoy!