"Curiosity is the very basis of education and if you tell me that curiosity killed the cat, I say only the cat died nobly." - Arnold Edinborough

Nginx is a constantly evolving web server rapidly growing in popularity. In July of 2013 nginx even managed to become the most used web server amongst the top 1000 sites ranked by traffic. It’s safe to say that these days you cannot afford to not know about nginx or how to use it. Unfortunately the nginx documentation is more of an API documentation than it’s an introduction to how nginx actually works. This post is aimed at correcting that by walking you through the most important parts of the nginx configuration file in a logical order.

The Basics of the Nginx Configuration

Nginx is in all fairness a fairly simple HTTP server, however, because most people come from Apache there are a few gotchas in the nginx configuration that people need to be aware of before they start using this web server. The most important is that nginx is a reverse proxy first and HTTP server second, its first concern is not files but rather URLs, this changes the way we have to configure nginx.

The first thing you need to know is that the nginx configuration file uses 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 documentation 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;
    return          301 $scheme://www.domain.com$request_uri;

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;

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


If you’re switching from Apache to nginx 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 documentation 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. To do this we use a regex location with a named variable and then issue our redirect.

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

    root            /home/domain.com;

    # This will match any URI beginning with /forum
    location ~ ^/forum/(?P.*)$ {
        return 301 $scheme://forum.domain.com/$1;

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; # This is not needed if you have cgi.fix_pathinfo = 0 in php.ini (you should!)

As mentioned 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; # This is not needed if you have cgi.fix_pathinfo = 0 in php.ini (you should!)

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[‘REQUEST_URI’]. 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 documentation you will notice that it takes a second argument. This will let you define how much information is output by nginx. A value of warn will give you sufficient info to debug most issues.

For more detailed reading on debugging please see my debugging nginx errors post.

My Book

If you liked the style of this blog post then check out my nginx book – Instant Nginx Starter. It introduces you to the syntax and flow of nginx and minimizes your time spent debugging errors.

  • MTecknology

    Posted: July 28, 2010

    Nice little jump start to understanding blocks and servers. Reply

  • Mitur Binesderti

    Posted: September 7, 2010

    It would be helpful to mention, at least once, where these configuration files go... Reply

  • MTecknology

    Posted: September 7, 2010

    The configs would go... where every other config goes... /etc/ unless you installed from source in which case that should be entirely obvious. Reply

    • Brent Gulanowski

      Posted: March 29, 2011

      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. Reply

      • fjordvald

        Posted: March 30, 2011

        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. Reply

        • Warren Wilkinson

          Posted: February 11, 2013

          So why not mention the default? I'd bet a large portion of your users aren't compiling from source and are instead using whatever ubuntu or debian or redhat gives them. For 90% of people, that's /usr/local/nginx/conf/nginx.conf. So mention it. Reply

          • Martin Fjordvald

            Posted: February 11, 2013

            Because there are too many variations for me to give a default and the people SHOULD know the most standard ones. Where config files are placed is not at all about nginx, it's about their OS. This is a primer about nginx and I'm not going to pretend I know how every OS out there install it.

            If you use CentOS and you do yum install x then you should know it installs config into the /etc/ directory. If you don't then you need to go back to basics and learn about your OS first. I specifically avoid talking about it because I don't want to cover your OS unless it directly pertains to something nginx, which default location for config file doesn't.

            If you compile from source then you should be familiar with the compile flags that specifically states right there in --help where the config will be placed. If you aren't then you shouldn't be compiling from source.

        • Tobias

          Posted: April 28, 2014

          Thankfully at least the discussion pointed me to the right directory.
          Apart from that the article was really helpful. Reply

          • Oskar

            Posted: March 23, 2015

            Thanks Warren, I needed a pointer in the right direction. Kudose to you for bringing up the subject!

      • MOGH

        Posted: May 21, 2011

        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:
        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. Reply

      • Baylink

        Posted: November 15, 2011

        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. Reply

      • Tyrone Williams

        Posted: December 11, 2011

        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 Reply

  • Ben

    Posted: September 8, 2010

    @ 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 Reply

  • incel

    Posted: September 22, 2010

    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.- Reply

    • fjordvald

      Posted: September 27, 2010

      The Nginx wiki has loads of examples, if you need help you should join us in the #nginx IRC channel on irc.freenode.org Reply

    • Johnny Peck

      Posted: November 30, 2010

      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. Reply

  • Kevin McCaughey

    Posted: February 5, 2011

    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! Reply

    • fjordvald

      Posted: February 10, 2011

      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. Reply

  • Johans

    Posted: February 27, 2011

    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/ Reply

    • fjordvald

      Posted: March 2, 2011

      Thanks, glad you liked it.
      And the link has now been updated, thanks for reporting it. Reply

  • Giuseppe Russo

    Posted: April 16, 2011


    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 www.mywebsite.com and not www.mywebsite.com/forum or 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 Reply

    • fjordvald

      Posted: April 17, 2011

      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. Reply

  • Michiel Roos

    Posted: May 19, 2011

    For the PHP block use:
    try_files $uri =404
    instead of the if statement. Reply

    • fjordvald

      Posted: May 20, 2011

      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. Reply

  • Danilo

    Posted: May 24, 2011

    Thank you a lot, happily flattr'd :) Reply

  • why

    Posted: June 7, 2011

    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? Reply

    • fjordvald

      Posted: June 7, 2011

      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. Reply

  • Dan Dascalescu

    Posted: June 28, 2011

    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 to sudo service nginx stop, then kill the nginx pid, then sudo service nginx start. Reply

    • fjordvald

      Posted: June 30, 2011

      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. Reply

  • Glenn

    Posted: August 26, 2011

    Brilliant, thanks Martin, very helpful. Reply

  • nikita

    Posted: January 20, 2012

    what is the meaning of '^' in the line below?

    rewrite ^ http://www.domain.com$request_uri? permanent;

    thanks! Reply

    • fjordvald

      Posted: January 21, 2012

      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. Reply

  • scott

    Posted: May 6, 2012

    this is an old post but this works for me on Centos 6

    # Generated by iptables-save v1.4.7 on Mon Feb 6 17:29:54 2012
    :INPUT ACCEPT [0:0]
    :OUTPUT ACCEPT [4:496]
    -A INPUT -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 443 -j ACCEPT
    -A INPUT -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 80 -j ACCEPT
    -A INPUT -p icmp -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
    -A INPUT -j REJECT --reject-with icmp-host-prohibited
    -A FORWARD -j REJECT --reject-with icmp-host-prohibited
    # Completed on Mon Feb 6 17:29:54 2012 Reply

  • lulalala

    Posted: July 6, 2012

    I am a bit lost in the paragraph where "This location block matches a URI that ends in .php but it does not care if it’s a file or not." Since you used try_files, nginx would actually care about existence of file. But that paragraph sounds as if it doesn't. Reply

    • mfjordvald

      Posted: July 6, 2012

      The location doesn't, which is why you need to make sure it is an actual file via try_files or to configure your PHP installation properly. Reply

  • daan

    Posted: August 15, 2012

    Nice one dude. GOing to try this tonight, Using perl dancer as my app and Starman as the back end server... hope it goes well Reply

  • zuzmoo

    Posted: August 21, 2012


    I tried to configure nginx with php5. I used this config: http://www.howtoforge.com/installing-php-5.3-nginx-and-php-fpm-on-ubuntu-debian
    but i get an error message if I try to load my info.php: file not found. There is a .html file in the same directory and it works fine. My php settings are wrong somewhere. In the nginx error log: "Primary script unknown" while reading response header from upstream.. Reply

    • mfjordvald

      Posted: August 21, 2012

      I cover that error here: http://blog.martinfjordvald.com/2011/01/no-input-file-specified-with-php-and-nginx/ Reply

  • kevin

    Posted: August 29, 2012

    I have a ruby install on media temple (dv) 4, plesk 11; If i add the correct locations block to one of the plesk generated files, the site works fine. The problem is if someone reconfigures plesk, plesk will auto-generate the files it needs and overwrite. I included nginx.conf in /var/www/vhosts/domain/conf, but it's not being picked up?

    Any ideas? Reply

    • mfjordvald

      Posted: August 31, 2012

      Plesk is a control panel that takes over administration of your server. That means stuff has to be done The Plesk Way. Therefore, ask Plesk for support as they're the ones who know best. Reply

  • prathima

    Posted: November 5, 2012


    Can you please tell me how to configure nginx as http1.1 server.
    what changes should i make it to use http1.1 version in my configuration file.

    your help is really appreciated.

    thanks Reply

    • mfjordvald

      Posted: November 5, 2012

      First of all you need to ensure you have a new enough version. Then you need to enable it with http://www.nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version
      I'd also recommend experimenting with http://www.nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive after that. Reply

  • Carsten

    Posted: November 18, 2012

    Thanks for the nice starter here.

    Would be nice to cover 404 redirects as well. Might interest some people.

    thanks from germany Reply

  • Mohsen

    Posted: January 7, 2013

    Very good!

    You just saved with "SEF URLs" section.

    Thanks from Iran. Reply

  • David

    Posted: February 25, 2013

    I popped over here from the Nginx wiki -- thanks for taking the time to explain things. I switched from Apache to Nginx about a year ago and I'm still finding things I can do to increase performance, there's just so much to know. I was admiring how this theme displays code, so I peaked under your hood (excuse the invasion), looked up your theme -- a typography theme, no wonder. Looks great! Reply

  • name

    Posted: July 10, 2013


    "The configs would go... where every other config goes... /etc/"

    Wait... except for the ones in /opt ... well, /etc or /opt or actually pretty much any directory a developer chooses since there's nothing aside from convention to stop him.

    Anyways, one doesn't dive straight into this tutorial. One must first take the tutorial for the tutorial :) The it's all clear. Reply

  • Joe

    Posted: November 6, 2013

    This is a nice primer, except (considering it's a primer) you haven't mentioned where to actually *put* the configuration files. That would be very convenient to know. Reply

    • Martin Fjordvald

      Posted: November 6, 2013

      Please see this comment which deals exactly with that and why I don't cover configuration file location: http://blog.martinfjordvald.com/2010/07/nginx-primer/#comment-15680 Reply

  • John Evans

    Posted: April 27, 2014

    I read this post as I'm looking at trying Nginx out and switching from Apache. I thought the article was well laid out and explained a lot. I then got on to reading some of the comments and was instantly reminded of a post I wrote about the commenting habits of some people and it was a general rant people that only offer negative criticism.

    It was the part of the comments section dealing with the location of the config file that I was looking at. The author (Martin Fjordvald) has taken the time to write this post for the benefit of Nginx newbies like me and it is valued enough to be included on the Nginx wiki. I find it shocking that people can only see the negative aspects and cannot be bothered to get off of their arse and find out where the config file lives by themselves.

    Brent Gulanowski commented on what makes a good writer. Well Mr. Gaulanowski, I was a military instructor in the British Army for 10 years in subjects ranging from practical to theory and I can tell you that the mark of a good commenter is giving constructive feedback without discouraging the subject from giving his full effort on his next attempt.

    Rant over,
    :-) Reply

  • Woody

    Posted: May 5, 2014

    If this is a primer, can you explain everything when you use it. You spring "try_files" on me. What's that? You didn't define what that does. I might as well just go read the documentation first. Sorry for being annoyed sounding. Just trying to help you be a better primer writer. Reply

    • Martin Fjordvald

      Posted: May 6, 2014

      Hi Woody, you are right, my primer does assume a minimum reader engagement. I'm trying to enhance the documentation where I find it lacking - I'm not trying to replace it. You are expected to go read the documentation for more details on some of these directives. Reply

  • jswright61

    Posted: June 9, 2014

    I am curious about the default_server flag. If I have multiple conf files in sites-enabled (Using apt-get package on Ubuntu, all conf files in /etc/nginx/sites-enabled are included) what is the order of operation? Are all server blocks scanned in all conf files before default_server is used? Or must default_server be defined in the last conf file in the directory (sorted alphabetically)?

    And does *.mydomain in a server_name definition match before the default server? Reply

    • Martin Fjordvald

      Posted: August 7, 2014

      Included files are not sorted so for all intents and purposes they can be considered in random order - as in it will be consistent but you cannot predict the order. Nginx uses in-place inclusion and then parses the config file as if it was one file so it doesn't matter where the default_server flag is specified.

      Server_name directive does indeed match before default_server yes. Default_Server is a fall back when there is no matching ip:port and server_name combination. Reply

  • christian

    Posted: June 21, 2014

    Awesome writing Martin. Thanks so much for penning it in such a "human-understandable" way. Great stuff.

    I'm trying to compare and contrast your SEF location block with the one found in Step 4 of the following digital ocean guide https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu-14-04

    Looks like they're both two different beasts. My gut says "trust Martin, his is the better method." Yet my brain says, "the digital ocean guide is a bit newer (4 years), so perhaps those are "better practices."

    This is my own box with my own php scripts so I'm only worried about best coding practices, not making "everything" compatible. Hence, I like your comment: "PHP will then have the full URI in $_SERVER['PATH_INFO']. Simple, elegant and easy to understand."

    As long as I can access the $_SERVER array, I think I'm good.

    Can you shed any light on a compare/contrast between these puppies? Thanks man!

    PS: I'm a beginner, clearly. So the more you can explain "why" the better. If you want me to be more specific in my questions, lemme know. Reply

    • Martin Fjordvald

      Posted: August 7, 2014

      I actually update my posts fairly regularly for new practices and corrections, the primer was last updated Friday, July 5, 2013. Though, I will update now since $_SERVER['PATH_INFO'] is actually not the ideal thing to use, $_SERVER['REQUEST_URI'] is.

      As for the differences between this and the Digital Ocean post? It's mainly just that my try_files block will send any request for a non-existent file to $root/index.php whereras theirs will show a 404 error. So say, /blog/post1 would be sent to $root/index.php with /blog/post1 as REQUEST_URI while in DO post it would result in 404. Reply

      • christian

        Posted: August 21, 2014

        Funny thing: I've visited this post at least 15 times in the last 15 days, and forgot I had a comment on it. I was surprised to see my name on it! :) Thanks for the reply Martin.

        So anytime there is an internal redirect of /index.php (as the last item in try files), the REQUEST_URI gets pushed through to php (in this setup)? What's the benefit to this? Won't it still just throw a 404 anyway per your try_files directive in the .php location block? Reply

  • Phoenix Hunter

    Posted: August 2, 2014

    Hi. This website is a super resource, but NGINX is quite from Apache, so I want to check how to use it:

    My requirements are simple. I have a load of about 12,000 hits per minute. The hits are not for entire pages, just URLs on the site that need to check a database, and if an entry is found, then do a 301 redirect to a destination URL.


    1. All static files with file extensions .gif, .png, .css etc can be static. Served with expires max. No problem.

    2. A couple of hard folders in the root of the site need to just pick up files from there. Important here is that I won't have a .php in the names of these files, they will be without an extension at all (to manage pretty browser URLs) but are all PHP files. In Apache world, I did this by putting a "SetHandler" or "DefaultType" to x-http/php or something.

    3. Apart from that complication, everything else from the root of the site will be a quick check to a specific program. I need to pick up the part of the URI after the slash and then send it to this program.

    To express visually #2 and #3 above:

    http://example.com/folder/1 -> will just show the file "1" which is actually /folder/1.php

    http://example.com/folder/9 -> will just show the file "9" which is actually /folder/9.php

    http://example.com/abc -> this doesn't begin with "/folder" so we need to check the database for a record for "abc", this check will be done via a program called check.php which is the root folder, but this program is never exposed to the public. If there is a record for "abc"...then do some action.

    Currently in the Apache world I manage this with .htaccess. I think there's an easier way to do this with server/location blocks. Any tips much welcome!

    Thanks! Reply

  • DaAwesomeP

    Posted: August 17, 2014

    I am receiving this:
    nginx: [emerg] pcre_compile() failed: unrecognized character after (?P in "^/forum/(?P.*)$" at ".*)$" in /etc/nginx/nginx.conf:85
    Nginx 1.7.1
    I tried before with 1.6 thinking it had an older or newer PCRE in it than you had when the example was created. Reply

  • christian

    Posted: August 21, 2014

    Hi Martin,

    Thanks for all the help and the great book! Still struggling, but I bet I'm gonna get it as long as you're around! :)

    Regarding line 9 in the final SEF URLS section:

    I'm not understanding how that line will "rewrite" an extensionless url to one with .php appended so nginx can process the file. Or maybe that's not what it's supposed to do?

    Consider this config: http://pastebin.com/7eNiBDZ1

    Now let's run through some real requests:

    a) http://tripod.dev/test.php > Everything works great.

    b) http://tripod.dev/test > 404 Not found.

    c) http://tripod.dev/test.php?h=yes > Cool. I'm able to access $_GET['h'] in my php script

    d) http://tripod.dev/test?h=yes > Nope. Nothing.

    How do I make sure the $query_string gets into the php block even when there are rewrites? Maybe d) should never work. But b certainly should. What am I missing (besides the superior fjordvald gene)? :) Reply

  • DSdavidDS

    Posted: January 17, 2016

    Great tutorial. I was looking all over on how to make SEF urls and this guide included that along with an easy guide on enabling php too!

    I found it interesting that even if this article is nearing 6 years old, the information is still all relevant! Reply

    • Martin Fjordvald

      Posted: January 20, 2016

      Thanks! All the information is still relevant because I make sure to keep the post updated. :) Reply

  • Robson Sobral

    Posted: April 6, 2016


    Still really useful, indeed!

    Do you mind if I ask something?

    I'm trying:

    location / {
    try_files $uri $uri/ $uri.html $uri.php$is_args$query_string;

    But I can't find a way to redirect all `.html` and `.php` to their extensionless versions and the `/index.php` and `/index.html` to `/`. I tried using `rewrite` but no luck.

    Can you help me, please?

    Thank you! Reply

    • Martin Fjordvald

      Posted: May 6, 2016

      Don't try to redirect /index.php to /
      Google will know just fine that it's the same. If you want to make super duper sure then use the canonical tag. Reply

  • Laramie

    Posted: February 25, 2019

    I know you wrote this a long time ago, but it's been helping me for like a week now! Great write-up, thank you! Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.