When I do something that is not trivial and needs some research, I try to summarize it and write it down, in case that I will need later to do the same thing again. It may also be useful for other people that could be trying to do something similar.

Monday, October 22, 2012

Using NGINX as a Web Server for Drupal

Nginx (engine-x) is a web server that is regarded to be faster than Apache and with a better performance on heavy load. The difference is summed up succinctly in a quote by Chris Lea on the Why Use Nginx? page: "Apache is like Microsoft Word, it has a million options but you only need six. Nginx does those six things, and it does five of them 50 times faster than Apache."

Technically speaking, Apache is a process-and-thread-driven application, while Nginx is event-driven. In practice this means that Nginx needs much less memory than Apache to do the work, and also can work faster. There are claims that Nginx, working in a server of 512MB RAM, can handle 10,000 (yes, ten thousands) concurrent requests without problem, while Apache with such a load would just commit harakiri (suicide). Besides, the configuration of Nginx, once you get used to it, is simpler and more intuitive than that of Apache.

It seemed like something that I should definitely give a try, since my web server already had performance problems and I cannot afford to pay for increasing its capacity. Here I describe the steps for installing and configuring Nginx to suit the needs of my web application (which is based on Drupal7, running on a 512MB RAM server at Rackspace).

7. Full configuration of the site

A full version of the file /etc/nginx/sites-enabled/btranslator_dev looks like this:

server {
listen 80;
listen 443 ssl;
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
server_name dev.btranslator.org l10n-dev.org.al;
root /var/www-ssl/dev.btranslator.org;
access_log /var/log/nginx/btranslator_dev.access.log;
error_log /var/log/nginx/btranslator_dev.error.log info;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# This matters if you use drush
location = /backup {
deny all;
}
# Very rarely should these ever be accessed outside of your lan
location ~* \.(txt|log)$ {
allow 127.0.0.1;
deny all;
}
# This location block protects against a known attack.
location ~ \..*/.*\.php$ {
return 403;
}
# This is our primary location block.
location / {
index index.php;
try_files $uri $uri/ @rewrite;
expires max;
}
# This will rewrite our request from domain.com/node/1/ to domain.com/index.php?q=node/1
# This could be done in try_files without a rewrite however, the GlobalRedirect
# module enforces no slash (/) at the end of URL's. This rewrite removes that
# so no infinite redirect loop is reached.
location @rewrite {
rewrite ^/(.*)$ /index.php?q=$1;
}
# If a PHP file is served, this block will handle the request. This block
# works on the assumption you are using php-cgi listening on /tmp/phpcgi.socket.
# Please see the php example (usr/share/doc/nginx/exmaples/php) for more
# information about setting up PHP.
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
# Intercepting errors will cause PHP errors to appear in Nginx logs
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php-fpm.sock;
}
# The ImageCache module builds an image 'on the fly' which means that
# if it doesn't exist, it needs to be created. Nginx is king of static
# so there's no point in letting PHP decide if it needs to be servered
# from an existing file.
# If the image can't be served directly, it's assumed that it doesn't
# exist and is passed off to PHP via our previous rewrite to let PHP
# create and serve the image.
# Notice that try_files does not have $uri/ in it. This is because an
# image should never be a directory. So there's no point in wasting a
# stat to serve it that way.
location ~ ^/sites/.*/files/imagecache/ {
try_files $uri @rewrite;
}
# As mentioned above, Nignx is king of static. If we're serving a static
# file that ends with one of the following extensions, it is best to set
# a very high expires time. This will generate fewer requests for the
# file. These requests will be logged if found, but not if they don't
# exist.
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
# Configuration for phpMyAdmin
location /phpmyadmin {
root /usr/share/;
index index.php index.html index.htm;
location ~ ^/phpmyadmin/(.+\.php)$ {
try_files $uri =404;
root /usr/share/;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /usr/share/;
}
}
location /phpMyAdmin {
rewrite ^/* /phpmyadmin last;
}
}