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.

  • 101DoFollowBlogs

    I’ve heard some good things about this blog. Remember to balance the pics with the text tho. cheers!

  • http://distroworld.co.cc car9

    hi;

    what if we want run SSL on our virtual host? how to configure it? Thx

    distroworld.co.cc

  • Peter

    You can either let nginx handle the SSL communication or pass SSL traffic directly to the Apache server.

    Take a look at the nginx SSL documentation: http://wiki.nginx.org/NginxHttpSslModule

  • https://blog.creativeprogramming.it Stefano Gargiulo

    Thank you very much.

    It was very useful to get rid of apache and to boost up it where i can’t get rid of it.

    Just an improvement:

    the regex to detect images and static content is wrong: you should escape the “.”

    ~* ^.+.(jpe?…
    becomes:
    ~* ^.+\.(jpe?…

    if you don’t add this you can get errors in urls like:

    http://www.mysite.org/fantastico
    because it ends with something followed by “ico”

  • Peter

    Thanks Stefano for mentioning this typo. I corrected it in the article.

  • http://domainsoutlook.com Vish

    Hi,

    How are you?

    I am having permission denied errors in the error log with nginx. Did you change the permissions on the file or yours worked fine in the your settings. Can you please suggest a fix around this issue?

    Thanks.

  • Peter

    Check the setting “user” in the nginx.conf file. You should assign nginx the same user and group as apache is running with.

  • http://www.ph-v.net Phil

    Hi,
    Thank you for that interesting article.
    But I’m a bit surprised: AFAIU Apache and NginX listening on the same TCP port (80)?
    Sounds weird, doesn’t it? unless this is done by binding Apache on the loopback? does it work without any problem?
    Thanks. Cheers,

  • Peter

    They can listen on the same port if they use different interfaces. So Apache listening on the loopback and Nginx on eth0 is working fine.

  • as

    That’s correct! :)

  • Michael

    Awesome tutorial! Thank you! I had some problems getting everything up and running, but if it wasn’t for your help I would have gotten started.. :D

  • Pingback: Bookmarks for March 6th | Chris’s Digital Detritus

  • ofree

    Hi, this article was helpful.

    I do the same thing but with different condition. Since I do it on my hosting servers, so I need to ask about the file permission for the root directory. (or any other solution I guess).

    My problem is I found error 403 when setting up nginx and apache to the same document root (htdocs) which is owned by other OS user. Let me say like this :

    htdocs is : “/home/foo/foobar.com/htdocs”
    owner of htdocs is : foo:foo
    while user to run apache n nginx is already the same : running as nobody:nobody

    on the error log show like this when I try to access some images :

    [error] 32302#0: *6 openat() “/home/foo/foobar.com/htdocs” failed (13: Permission denied), client: xxx.xxx.xxx.xxx, server: foobar.com, request: “GET /img/themes/classic/images/wall.png HTTP/1.1″, host: “foobar.com”, referrer: “http://foobar.com/catalogue”

    First thing I thought is I have some problem on file permission, but I wonder why apache is not having such problem (I already try to pass all requests to apache), so I’m not yet changing anything to the file permission.

    My nginx.conf is also the same :

    server {
    listen XXX.XXX.XXX.XXX:80;
    server_name foobar.com http://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 /home/foo/foobar.com/htdocs;
    }
    }

    Do you have any idea what should I do with the file permission on the htdocs?

    Appreciate any idea. Thanks

  • Peter

    In my opinion the easiest option would be to run nginx as the user nobody:foo and give read and execute (on directories) permissions for the htdocs folder to the group foo.

  • Pingback: Virtualmin, Apache, and Nginx Reverse Proxy – - All-in-One Discussion Portal | Howtos | Tips & Tricks @ DiscussWire.comAll-in-One Discussion Portal | Howtos | Tips & Tricks @ DiscussWire.com

  • Edmond

    Great article. But I encountered some problem on some site like phpMyAdmin (it is configured at apache backend), the nginx web front sometimes redirect to e.g http://www.example.com:8080/phpMyAdmin... (notice the internal apache port) causing web to break. What could be the cause?

  • Peter

    For most applications you can usually set the web address in a configuration file.

  • Ben

    Hi !

    Thank you for your article.

    I tried to configure my server in this way but it doesn’t work.

    When I try to access my website with a browser, it can’t find the static content (404 http error). For exemple, the web page is load but without css, image, js, etc..

    Do you have any idea about this problem ???

  • Peter

    Possible the path in your location directive for css,js,… is wrong. Check the path and if it does not work even with the correct path, remove the location directive.

  • Ben

    It’s OK, I had an error in my default vhost file.
    Thanks a lot for your help :)