Update: There is now a follow up to this post which deals with the differences 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, the location directive wiki entry explains 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 the error log directive in the wiki you 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
The official nginx wiki is an invaluable resource, a good way to expand your knowledge about Nginx is to read the directives and variables in the Core module. Also see the full config example to get an idea of a more complex configuration file.
Related posts:
Nice little jump start to understanding blocks and servers.
It would be helpful to mention, at least once, where these configuration files go…
The configs would go… where every other config goes… /etc/ unless you installed from source in which case that should be entirely obvious.
One of the marks of good writing is not assuming that your audience knows what you think to be “obvious”. If your starting assumption is that your audience should be smart enough to know X then you are already excluding people unnecessarily. The whole point of writing is to share knowledge. Might as well do a complete job. Doesn’t hurt to be redundant. If you don’t want to do that, provide pointers for prerequisite knowledge. It’s the right thing to do.
The problem is that configs doesn’t actually “go” anywhere. The default location of configuration files is /usr/local/nginx/conf/nginx.conf. But this with a source compile, then you have compile flags which changes the location and you have init scripts which can use the -c flag to specify which config file to use. And it all differs depending on whether you compile from source or use a repository.
And you really do have to stop at some point, you can’t assume people doesn’t know anything at all. Do we then also have to tell people how to use their mouse? Covering how their repository install thing isn’t something I’m capable of nor interested in, and if people compile from source then they should be able to figure out where stuff installs.
Even in school someone has to make the effort to understand when learning something new to them. Sometimes that means *going through the motions* with trying, but accept the failures too. Then moving forward to ask questions based upon some effort already made.
A good place to start an understanding is here:
http://wiki.nginx.org/InstallOptions
Nginx, it is a very complex web server and has many options. I believe it is fair to say, for someone to try to give a complete understanding to everyone is asking a bit much in my opinion. With the information here and other places with some effort, someone will start to gain an understanding by working through it.
Hope this helps.
Nerdview can be troublesome, yes. But in the case of “Where do the config files live?”, the true answer is “wherever the heck your distro’s packager thought they ought to go”: package authors are notoriously bad about that.
In CentOS 5, frex, they go where I would expect to find then: /etc/nginx, which just means that the CentOS architects and I have similar reflexes.
The *real* answer is “install whatever package on your distro supplies locate, and do # updatedb after you install the package”. If you’re going to teach people something, *that* is what it ought to be.
To Mr. Martin Fjordvald, the author – Thank you so much , I just wish you were #1 on google and I didn’t waste time on other instructions which confused the shit out of me. Thank you. wordpress finally is working and urls look pretty. thanx!.
Below I address the idiots who bad mouth this article as too complex.
————————————————-
@Brent Gulanowski – I spent over 2 days , 10 hours each trying different instructions to get this specific part of the installation working. I was about to give up and pay $15, until I found this page that’s written in plain language, that explained the concept of those blocks and their arrangement which allowed me to adapt my config and get it to work(php-fpc/nginx/ubuntu/sql/apc/memcache plugin/wordpress) that 18 other sites did not do (or did but the explanation went over my head).
I never seen a Linux until yesterday morning, and I figured out where the config file is by looking around and first getting familiar with sudo and nano and mv and command prompt.
——–
Thank you again Mr. Martin Fjordvald
@ Mitur
This is the equivalent of more less (web.config in the windows world)
it goes under etc/nginx/sites-available/domain.com if you installed nginx from a repository
Following the excellent guide posted in cyberciti.biz (http://www.cyberciti.biz/faq/rhel-fedora-install-configure-nginx-php5/) I istalled nginx
in centos 5.5 successfully, but I fail to configure VirtualHosts.
I tried different combinations but it does not work.
Could you please help me?
I just need an example of a working configuration.
I really appreciate it.
thank you.-
The Nginx wiki has loads of examples, if you need help you should join us in the #nginx IRC channel on irc.freenode.org
Make sure you have set your host names in the hosts file (/etc/hosts). If your localhost, default configuration is working then this is the most likely reason you can’t get to your virtual hosts. Hope that helps.
Thanks for your Blog Martin, it has been of great use to me whilst setting up nginx etc on my Linode. One thing I would love to see you do is an article on how (as on overview) all this stuff works together and what does what. e.g. in a PHP/MYSQL/NGINX/fpm etc setup, what each of the modules do and how they work together. There are a lot of setup guides out there, but I have never really understood the “why” of what I am doing, apart from the Nginx configuration, which I now understand better thanks to your blog.
I think you would be a good person to explain it!
Thanks again for your blog and keep posting!
Thanks for the kind words. I’m not really sure if a post on this subject would be very useful as I tend to have an unorthodox setup, I rarely use yum/apt-get to install things like MySQL, PHP etc.
Thanks for a nice intro to Nginx. I’ll read the Packt book you reviewed next.
BTW – the link to your Nginx/Apache post is broken – path is 2011/01, it should be 2011/02:
http://blog.martinfjordvald.com/2011/02/nginx-primer-2-from-apache-to-nginx/
Thanks, glad you liked it.
And the link has now been updated, thanks for reporting it.
Hi,
I read a couple of months ago in a hosting forum that its better to have a forum, a blog or other scripts in the root like http://www.mywebsite.com and not http://www.mywebsite.com/forum or http://www.mywebsite.com/blog because there will be problems with NginX.
Sorry but don’t remember that forum were hosting support wrote about that NginX problem.
Is that correct?
Does NginX require to install a blog in a subdomain like http://blog.mywebsite.com?
I have websites with the blog in a folder and would like to know before installing NginX.
Maybe your article explains this but I don’t understand well, English isn’t my first language.
Giuseppe
Hi,
There’s nothing to this rumour. Nginx will work no matter where you place your code so long as your configuration reflects this properly. The only problematic case is if you have PHP code in different roots, say some code in /home/user/public_html and some code in /var/www. It’s still perfectly do-able but you then have to use multiple location blocks to capture PHP requests and make sure that PHP is sent the proper path.
The reason I have the blog part as a subdomain is just that I prefer it and find it more organized than having it all as part of my domain root.
For the PHP block use:
try_files $uri =404
instead of the if statement.
I understand where you’re coming from with if being evil and all that. However, the wiki is a bit “dramatic” for lack of a better word. If is quirky and unpredictable, however, it’s consistent. So in this case there’s nothing wrong with using an if to check for file existence.
Whether you use if or try_files you’ll still have a stat() call to check for existence. You’ll functionally have the same outcome as well, in fact the only difference is that try_files will re-evaluate the location matching while if won’t. So there’s actually a tiny itsy bitsy advantage to using if over try_files.
Thank you a lot, happily flattr’d
I don’t get it. In Nginx’s very own official pitfalls section, it states very clearly that “listen 80;” is a bad thing to do. Yet, it’s added in almost every config I can find on the internet. Did I miss something?
The pitfalls page is over-zealous in my opinion. It’s not trying to say that it’s “bad” but rather that it’s “unnecessary”. The meaning of the two things are completely different but the wiki doesn’t really care. I’ll talk with the other prominent wiki people and see if we can have it changed.
Thanks for the intro!
It would help to mention how to properly restart nginx for the config changes to take effect. I’ve seen
sudo kill -HUP `cat /usr/local/nginx/logs/nginx.pid` recommended, but sometimes (I'm not sure why), that didn't exactly work, and I had tosudo service nginx stop, then kill the nginx pid, thensudo service nginx start.That’s an OS issue, not a Nginx one. I really do not want to open up that can of worms. Sending HUP to Nginx will always make the workers reload the configuration. The only cases where you need to restart if you need the master process to do something such as bind to port 80.
Brilliant, thanks Martin, very helpful.
what is the meaning of ‘^’ in the line below?
rewrite ^ http://www.domain.com$request_uri? permanent;
thanks!
Rewrite takes a regex to match against the URI, but since I don’t care about what the URI is I just use a ^ which means “start of string” and thus matches everything.