Monday, September 19, 2011

Using Nginx as a Reverse Proxy

http://www.ubuntugeek.com/using-nginx-as-a-reverse-proxy-to-get-the-most-out-of-your-vps.html
This how-to is going to assume that you already have a standard LAMP stack running, if not, there are plenty of tutorials and examples on how to get a LAMP stack running.

We have chosen to go with a standard Apache 2 installation using libapache2_mod_php5 over using Nginx (pronounced ‘Engine X’) with fastCGI. You may be thinking “that is preposterous, why not just use Nginx as your webserver and ditch Apache?” I hate fastCGI, I’ve never had very good luck with it and most importantly I know Apache. Also, when all Apache has to worry about is the dynamic content, in this case PHP, it is quite fast, and has a smaller memory footprint then normal Apache usage.

Using Nginx as a reverse proxy is great for a few reasons. Firstly it handles static content very well. It is able to handle the requests and serve static content much faster in our tests and this has cut our page load time in about half (using YSlow with a clear cache). The memory footprint of Nginx is very small so this extra speed increase is worth every megabyte, in this case .6 megabytes of our total ram on a 540 megabyte server. Secondly, it allows for quick and easy migration of your Apache services to another server. Through the config files you are able to specify an IP of your server and a port. If your apache server is taking a pounding it wouldn’t be difficult to move it to another server and just change the proxy IP to your now remote server.

Setting up Nginx is fairly straight forward. I will be showing the commands for an Ubuntu 8.04 installation but they should work for previous versions and other distributions (with a little tweaking).

Install Nginx

sudo apt-get install nginx
the following is essentially the config file we use for /etc/nginx/nginx.conf and is pretty similar to the default config for the installation. The only major thing I changed was adding gzip compression. I have personally set the level to 5 although it is adjustable. The higher you set this value the more CPU intensive it becomes.

user www-data;
worker_processes  2;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    access_log /var/log/nginx/access.log;
server_names_hash_bucket_size 64;
    sendfile        on;
    tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay        on;
 gzip              on;
  gzip_comp_level   5;
  gzip_http_version 1.0;
  gzip_min_length   0;
  gzip_types        text/plain text/html text/css image/x-icon
 application/x-javascript;
  gzip_vary         on;
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
You are going to want to make sure that the line “include /etc/nginx/conf.d/*.conf;” is included in this config. This is where you will store the default proxy information.

We will now configure the default proxy. Create the file proxy.conf in the /etc/nginx/conf.d/ folder with the following contents.

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;
client_header_buffer_size 64k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffer_size   16k;
proxy_buffers       32   16k;
proxy_busy_buffers_size 64k;
The only thing you may want to change is the buffer sizes. We had to increase our proxy_buffer_size and a few others, from the default, to allow for larger cookies that were choking Nginx. With that being said you may want to decrease the buffers a bit, just do some testing. If the buffers are not working for your content Nginx will throw a 50x error.

Finally, we will configure the various hosts. To save yourself some time, and possible a headache you should always keep your static assets in one folder, while subdividing them by type in containing folders. For example static->images, static->js, static->css. This will greatly simplify your Nginx installation and I’ve found your code.

Now we will edit (with your favorite CLI editor) /etc/nginx/sites-available/default.

server {
 listen   80;
 server_name  example.com;
 access_log  /var/www/example.com/log/nginx.access.log;
 error_log  /var/www/example.com/log/nginx_error.log debug;
 
 #set your default location
 location / {
  proxy_pass         http://127.0.0.1:8080/;
 }
 
 #I had a problem accessing phpmyadmin with an Nginx reverse
 #proxy without adding this location
 location /phpmyadmin {
  proxy_pass         http://127.0.0.1:8080/phpmyadmin;
  allow 1.1.1.1;
  deny all;
 }
 #set your static folder location without the proxy pass so Nginx
 #will server those files. We also set expires max to add an
 #expires to have the client cache the files.  You will
 #have to  #set a version on your css and js files to prevent
 #the user who has cached files from not receiving new versions.
 location /static {
  root   /var/www/example.com/htdocs/;
  expires     max;
  }
  #error_page  404  /404.html;
  # redirect server error pages to the static page /50x.html
 #
 error_page   500 502 503 504  /50x.html;
 location = /50x.html {
  root   /var/www/nginx-default;
 }
}
#If you have a subdomain you need to add a new server if you
#want Nginx to server the static files. Our subdomain only
#serves static files so we have not set up a proxy_pass
server {
 listen 80;
 server_name subdomain.example.com;
 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
  root /var/www/nginx-default;
 }
 access_log /var/www/subdomain.example.com/log/nginx.access.log;
 error_log /var/www/subdomain.example.com/log/nginx.error.log;
 index index.html;
 location / {
  expires     max;
  root /var/www/subdomain.example.com/htdocs/;
 }
}
Finally we need to make some quick changes to Apache and we’ll finally have everything running. Edit the file /etc/apache2/ports.conf.

You’ll want to change the listen line to 127.0.0.1:8080. This will prevent apache from receiving requests from outside, but you should be blocking port 8080 anyways! The port we’ve set is 8080 but whatever you set in the Nginx configs is what you should use.

NameVirtualHost *
Listen 127.0.0.1:8080
Lastly, if you don’t want all your apache logs to show 127.0.0.1 for who is accessing your files or your application uses IP’s to track sessions you need to install libapache2-mod-rpaf. It is painless just issue the command below.

sudo apt-get install libapache2-mod-rpaf
reload or restart both Apache2 and Nginx.

/etc/init.d/apache2 restart
/etc/init.d/nginx restart
To see if it is working open a page on your website, if you don’t see any errors that is a good start. You can then check the logs of Apache and Nginx. Your Apache logs should only contain the php requests and your Nginx logs should contain all of your assets. Your Apache logs should also have HTTP 1.0 request when they go through the reverse proxy.
-------------------------

A couple of clarifying questions would be helpful before jumping into the fray:
1. I’m not seeing any declarations of the types of static files that Nginx would handle in your examples unless it is the “application/octet-stream” or the fact that you are gzipping the static files and that is where they are declared.
2. You mention it a wise choice to organize static files together in structures like
“/static-folder/static-file.img-type”
Is this “required” for Nginx or simply “better”? And either way, does this imply that if Nginx is to serve those files, they have to be located there versus where a CMS might place them (for instance, WordPress placing uploaded images into the wp-content/uploads/ directory)?
Thanks again. Looking forward to your thoughts on the above.
---
Here is the thing, the two are directly related which is why I said it is important to have a static folder.
location /static {
root /var/www/example.com/htdocs/;
expires max;
}
These few lines tell Nginx that everything in the static folder it should handle by finding in the /var/www/example.com/htdocs/ folder. So anything coming into such as /static/css/style.css will be handled by Nginx because it is missing the proxy_pass command for this folder. Where as example.com/index.php will be passed to Apache.
This really works out well because if you only wanted Nginx to server your user uploaded images you would just have the location /wp-content/uploads/ command and apache would be passed everything else. I would definitely recommend having Nginx handle your css and js as we’ve seen great performance increases using this technique.
Overall it took me about 15-20 minutes to get setup and running.
---------------------------
I find the following configuration lines more useful instead of keeping my status files separate from dynamic in different folders:
Instead the following lines simply detect the static files file extensions and only proxy pass onto apache for php scripts:
server { # simple reverse-proxy
listen 80;
server_name domain2.com http://www.domain2.com;
access_log logs/domain2.access.log main;
# serve static files
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /var/www/htdocs;
expires 30d;
}
# pass requests for dynamic content to apache
location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/proxy.conf;
}
}
NOTE: I make nginx and apache root and docroots point to the same file dir:
Of course, both approaches work, just preference really.
-----------------
NginX+PHP-FPM
http://interfacelab.com/nginx-php-fpm-apc-awesome/
Nginx vs. Lighttpd
http://hostingfu.com/article/nginx-vs-lighttpd-for-a-small-vps

1 comment: