Update:There is now a follow up to this post which deals with thedifferences between Nginx and Apache, it is recommended reading if you come from Apache:

Nginx is a fairly simple HTTP server, though there are a few gotchas people need to be aware of before they start using this 8th wonder. The most important is that Nginx is a reverse proxy first and HTTP server second, it does not necessarily have a concept of file, this will change the way we handle our configuration a bit.

The first thing you need to know is that the Nginx configuration file is an inheriting-hierarchy, directives specified in a higher block will filter down to lower blocks as a default value, from this follows that we want to specify things in the top most hierarchy whenever possible. Since directives in top blocks filter down as default values it is still possible to override them in most cases.

There are 3 hierarchies which are usually referred to as blocks. The HTTP-block, the server-block and the location block of which the hierarchy goes like this: http -> server -> location.

Furthermore there are two special locations, an event block and the root which the event block and the http block reside in. Both of these contain only a minor amount of directives. The majority of your time will be spent in the other three blocks.

The blocks have a semantic meaning of sorts. The server block is what in Apache would be considered a virtual host. The location block usually referrers to the URI.

When using the official wiki the context keyword specifies in which block a directive may be used, as mentioned earlier it is usually recommended to specify the directive in the top most block.

 

Virtual Hosts

To begin with the most interesting directives are server_name and root. The former instruct Nginx to use this server block when the HOST header matches the value and the latter defines what to use as root when looking for files.

This forms the basic of our virtual hosts, an example would be:

server {
  listen 80;
  server_name domain.com *.domain.com;
  rewrite ^ http://www.domain.com$request_uri? permanent;
 }

server {
  listen 80;
  server_name www.domain.com;

  index           index.html;
  root            /home/domain.com
}

Here we have two virtual hosts. The first one is hit when domain.com or any subdomain of domain.com except for www is sent as the HOST header by the browser. The reason for this is that Nginx will always choose the most specific match, and if visting www.domain.com then this will match the second block precisely.

This also means you can create a default virtual host to catch all domains without a proper match. Thankfully this is as simple as adding the default_server flag to the listen directive. This causes all request without a HOST header or without another vhost matching the HOST header to be sent to this vhost instead. Examples are requests accessing the IP directly or if someone points a random domain at your IP. The server_name _; which you will see mentioned in a lot of guides means nothing and does nothing. It’s just a bogus invalid HOST header that can be used to make sure nothing ever matches it. You should be able to simply not define a server_name.

server {
  listen          80 default_server;
  server_name     _;

  index           index.html;
  root            /var/www/default
}

Locations

If you’re changing over from Apache then this is where you want to pay attention. Nginx typically does not use complicated rewrites – usually we can accomplish the same using a location block.

The most important things to note are that locations, with the exception of named locations, works on the URI without any query parameters and only one location block will ever be run. This is also why I recommend putting directives in the top most block. A root directive defined in location / will not be available in location /images – unless defined in the server block. I trust you see how defining things in the upper most block will prevent code duplication and headaches.

Another important point about locations is that, as with server_name directive, Nginx will use the most specific location block. There are a few rules for how specific various setups will be, thelocation directive wiki entryexplains this very well, so you should read that first.

Let us look at a few examples of how to use a location block. In this example we’ve run a forum on URI /forum/ and have recently moved it to a subdomain. We now need to redirect the old URLs to the new URLs.

server {
  listen          80 default;
  server_name     www.domain.com;

  root            /home/domain.com

  # This will match any URI beginning with /forum
  location /forum {
    # We capture the URI and redirect it to the subdomain.
    rewrite forum(.*) http://forum.domain.com$1 permanent;
  }
}

server {
  listen          80;
  server_name     forum.domain.com;

  index           index.php;
  root            /home/domain.com/forum;
}

The requests for /forum are now successfully transferred to our new subdomain while requests to files not in /forum will be served from our normal /home/domain.com root.

Handling PHP

PHP – or any backend really ties in well with locations, namely we can define a location block to catch all PHP files.

server {
  listen          80;
  server_name     forum.domain.com;

  index           index.php;
  root            /home/domain.com/forum;

  location ~* \.php$ {
    include fastcgi.conf # I include this in http context, it's just here to show it's required for fastcgi!
    try_files $uri =404;
    fastcgi_pass 127.0.0.1:9000;
  }
}

As said previously, Nginx does not care about files but rather locations and this is why I have a try_files directive inside the php block. This location block matches a URI that ends in .php but it does not care if it’s a file or not. Therefore a request for /forum/avatars/user2.jpg/index.php will be matched and sent to PHP, and if PHP is not configured properly PHP will then execute /forum/avatars/user2.jpg when /forum/avatars/user2.jpg/index.php doesn’t exist. This provides a huge security risk. Do note that this is not a bug in Nginx, it’s the intended behaviour and as such will not be “fixed”.

This can also be fixed on the PHP side by setting cgi.fix_pathinfo=0 in the php.ini file.

The end result, though, is that .php files which exist will be passed via fastcgi to our PHP processes running on port 9000.

The Finishing Touch – SEF URLs

This setup works, but all the craze these days is to have search engine friendly URLs for SEO reasons. Usually this involves quite a few rewrites, but with Nginx we can do it with just one line, provided the backend script is written in a sane way.

server {
  listen          80;
  server_name     forum.domain.com;

  index           index.php;
  root            /home/domain.com/forum;

  location / {
   try_files       $uri $uri/ /index.php;
  }

  location ~* \.php$ {
    include fastcgi.conf # I include this in http context, it's just here to show it's required for fastcgi!
    try_files $uri =404;
    fastcgi_pass 127.0.0.1:9000;
  }
}

Did you notice the change? It’s minimal really. The one try files line means that it will first try accessing the full URI, which means that a static file request will end here. Secondly it will try the full URI plus a slash, thus looking for a directory. Finally, if none of these are found it will send the request to /index.php and perform a new location match, which will of course hit our PHP location and fastcgi_pass the request. PHP will then have the full URI in $_SERVER['PATH_INFO']. Simple, elegant and easy to understand.

Debugging Requests

Nginx is a complicated server at times, thankfully we have an excellent error log available to us to help figure out where things are going wrong. If you check theerror log directive in the wikiyou will notice that it takes a second argument. This will let you define how much information is output by nginx. A value of info will give you sufficient info to debug most issues.

Further Reading

Theofficial nginx wikiis an invaluable resource, a good way to expand your knowledge about Nginx is to read the directives and variables in theCore module. Also see thefull config exampleto get an idea of a more complex configuration file.

No related posts.