"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

The Big Picture

So you’ve finally decided to make the switch from Apache to nginx. You most likely did this for performance reasons; perhaps all those blogs have been writing about how fast nginx is or perhaps your webmaster friends have been raving about how they can now handle a lot more traffic without spending money on hardware.

This is usually all true, but why exactly is nginx so much faster than the typical Apache setup of the prefork MPM and mod_php? The technical explanation is that nginx is a non-blocking event based architecture while Apache is a blocking process based architecture. To simplify it heavily the theory is like this:

Apache Prefork Processes:

  • Receive PHP request, send it to a process.
  • Process receives the request and pass it to PHP.
  • Receive an image request, see process is busy.
  • Process finishes PHP request, returns output.
  • Process gets image requests and returns the image.

While the process is handling the request it is not capable of serving another request, this means the amount of requests you can do simultaneously is directly proportional to the amount of processes you have running. Now, if a process took up just a small bit of memory that would not be too big of an issue as you could run a lot of processes. However, the way a typical Apache + PHP setup has the PHP binary embedded directly into the Apache processes. This means Apache can talk to PHP incredibly fast and without much overhead, but it also means that the Apache process is going to be 25-50MB in size. Not just for requests for PHP requests, but also for all static file requests. This is because the processes keep PHP embedded at all times due to cost of spawning new processes. This effectively means you will be limited by the amount of memory you have as you can only run a small amount of processes and a lot of image requests can quickly make you hit your maximum amount of processes.

Compare this to the nginx event based method.

Nginx Event Based Processing:

  • Receive request, trigger events in a process.
  • The process handles all the events and returns the output

On the surface it seems fairly similar, except there’s no blocking. This is because the process handles events in parallel. One connection is not allowed to affect another connection even if run simultaneously. This adds some limitations to how you can program the web server, but it makes for far faster processing as one process can now handle tons of simultaneous requests.

Remember those limitations, though? Yeah, they’ll affect how we, as users, have to use nginx. For instance, we can no longer embed PHP into our nginx process as PHP is not asynchronous. Essentially, if we embedded PHP into nginx the event based architecture of nginx would be rendered void as PHP would be blocking and connections would just pile up.

Since a web server isn’t all that desirable without the ability to use dynamic scripting languages nginx has support for various communication protocols such as FastCGI, SCGI and UWSGI. Conveniently enough, PHP happens to support FastCGI, which means we can still use PHP.

FastCGI Versus Embedding

The thing that confuses most first time users of something not Apache is that suddenly they have to actually handle the PHP part themselves. nginx takes a complete hands off approach to all dynamic scripting languages and reverse proxy situations. (more on this later)

With Apache you just configure it to use PHP, start it and forget about it. With Nginx you have to spawn a number of PHP processes and tell nginx to talk to them. This has both advantages and disadvantages.

The primary advantage is that you now have complete separation of your web server and PHP. If you change something in PHP you don’t have to restart nginx, only PHP. Similarly, if you want to restart nginx then PHP keeps running. Since nginx supports upgrading the binary and changing the configuration on-the-fly this means you can upgrade your web server or change its configuration without any down time at all.

The primary disadvantage is that you have to handle the spawning and process control of PHP yourself, or rather nginx won’t do it for you. A year ago this was a bit troublesome. You had to use a script called spawn-fcgi to spawn the fastcgi processes and then monitor them with something secondary like Monit. Alternatively you could use a patch to the PHP source code called PHP-FPM – quite literally FastCGI Process Manager. As of PHP 5.3.3 PHP-FPM is now part of the PHP core, which means you don’t have to patch the source code yourself, but can just set a compile time configure flag. Naturally, most distribution repositories have a PHP build with PHP-FPM enabled as well. So really, this disadvantage is barely a disadvantage any more as PHP-FPM is a very cool thing in itself. More on that another day.

A second disadvantage, which is heavily disputed is that since the PHP process is no longer embedded there is an overhead in talking to PHP. This is used as an argument by some people as for why you should use nginx to handle static files and Apache to handle the PHP files. Theoretically there is an overhead in talking with Apache as well, so I personally doubt there’s much of a disadvantage, but I don’t have the tests to back that up.

The Configuration Differences

Okay so we now know what the big picture is, time to understand how Apache and nginx differs on a configuration level. This is where we’ll actually spend most of our time so this is what is important to know about. I have to stress that it’s important to actually learn about nginx. Nginx has undergone a huge amount of development in the last few years and as a result there is a lot of different advice out on various blogs. Unfortunately, a lot of it is bollocks and should be removed as it is often down right wrong. In general, if a tutorial or guide is making heavy use of ifs then avoid it, there are far better alternatives such as try_files, I’ll cover that a bit in this section.

.htaccess

The biggest headache for a lot of people is that they no longer have access to .htaccess. There is no way to change your nginx configuration without issuing a reload command to nginx. The effect can be simulated by using include directives in the main nginx configuration file, but any change will not take effect until the configuration file has been reloaded. This is also the primary reason why nginx is not very suited for shared hosting, not even in a situation where you reverse proxy to Apache.

There is no alternative in nginx so if you want to use nginx this is one thing you have to live without, in the beginning it might be annoying but after a few hours you really won’t notice or miss it.

Apache & Nginx Rewrites

Typically in Apache you will specify your rewrites in .htaccess or at least in a global context so that they are always evaluated. nginx provides locations to prevent this exact thing. We don’t want to execute something that we don’t need to, so if we need to rewrite the URI example.org/forum/index.php?topic=3 into forum.example.org/index.php?topic=3 we should use a location to ensure we only execute this rewrite when absolutely required.

Another thing about nginx rewrites is that by default they are internal rewrites, which means that they won’t change the URI the browser sees. They will only do that if you specify the “redirect” or “permanent” rewrite flag or if you rewrite to an absolute URL including the http:// part.

Apache RewriteCond

Rewrite Conditions is something which doesn’t really exist in nginx. Something you might see in Apache is the following:

RewriteCond   %{HTTP_HOST}   ^example.org$   [NC]
RewriteRule   ^(.*)$   http://www.example.com/$1   [R=301,L]

The directly translated nginx equivalent would be

if ($host != 'example.org' ) {
    rewrite  ^/(.*)$  http://www.example.org/$1 permanent;
}

I took this from an actual tutorial out there. This is wrong for two reasons. Most importantly nginx does a lot of optimization to vhosts. If you use an if like this you miss out on that optimization as a request for example.org and www.example.org will both have to go to the vhost, then parse the if and then the regex. Less important is that you’re capturing data nginx has already captured for you.

The nginx way to do this is the following:

server {
    server_name example.org;
    return 301 $scheme://www.example.org$request_uri;
}

This way a request for example.org will not parse to the www.example.org server block, we won’t have to actually parse a regex since it’s just a starting character and nothing else, and the rewrite still uses the URI captured by nginx.

Another popular RewriteCond is the following:

rewritecond %{REQUEST_FILENAME}!-d
rewritecond %{REQUEST_FILENAME}!-f
rewrite ^ /index.php [L]

Basically, if the requested file doesn’t exist and isn’t a directory then do execute a rewrite, this is often used to implement pretty URLs. In nginx you’d do this like the following:

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

What this means is that it will first check if $uri exists, (in relation to your root directive) then it will check if $uri/ exists, which would be a directory. Finally, if none of these exists it will do an internal rewrite to index.php

A quick thing to note is that while ifs are usually discouraged there are some cases where they cannot be avoided. For example if you want to check the request method is GET before you send to the backend then you are forced to use if ($request_method = GET) This will usually work just fine and isn’t too bad considering there isn’t a more optimal way to do it.

Using Apache and Nginx Together

A common scenario is for people to use both Apache and nginx. They’ll have nginx handle the static files and then proxy the dynamic requests to PHP. This can be beneficial if you are not completely ready to ditch Apache, for instance if you have a legacy application relying on Apache or .htaccess files. There are also a few cases where nginx alone cannot handle all use cases. A common one is when people need a HTTP front end for SVN. The nginx WebDAV module supports only a limited set of the protocol so in this case you need to reverse proxy to Apache and let it handle it.

On the flip side, having Apache handle your dynamic requests introduces another layer of complexity in your server setup, causing more potential problem areas. Usually you don’t actually need Apache so there’s really no reason to keep it around as a crutch, in the long run it will benefit you more to use just one of them.

if you do decide to use both, and plenty of people do, then there are a few things you should know. I won’t cover how to configure nginx for this as the Wiki is plenty resourceful there. But keep this in mind:

When you reverse proxy to Apache, nginx will handle the connection, parse it and then create a new connection to Apache. It does not tunnel the connection so you have to specify which headers to pass on. Furthermore, the connection nginx establishes is using HTTP/1.0, and not HTTP/1.1. This means things such as chunked encoding is not supported.

To make your reverse proxy experience as easy as possible you should therefore keep a few things in mind.

  • Let nginx handle SSL and keep the connection between nginx and Apache in plain text. There is no reason to add the overhead of SSL unless you are afraid of a man-in-the-middle attack on your internal network.
  • Let nginx handle the gzipping of content. Don’t gzip on the Apache side as that will most likely cause you some major headaches.

If you want a more comprehensive guide for reverse proxying and are too lazy to read the wiki then see kblessinggr’s Apache and Nginx Together guide.

A Note on Shared Hosting

I mentioned earlier that nginx isn’t very suited for shared hosting. You might think that if you let nginx handle just static files while proxying non-existing files and dynamic files to Apache then you’ll be good. This isn’t quite the case, though. As an example, consider basic authentication, if you want to have basic authentication on a static file then you will need to add it in the nginx configuration, and users cannot do this without having to reload the configuration file. If you allow them to change it and reload it then they can make a mistake and ruin it for everyone.

Think very carefully about every aspect before you decide to use nginx in your shared hosting setups.

  • Willie Jackson

    Posted: February 7, 2011


    Your posts on NGINX are among the best on the web. Nice work, mate. Reply


    • Ian Chilton

      Posted: February 16, 2011


      I agree - an excellent post. Installation tutorials are useful but as are articles like these that explain more of the background and covers a few more advanced aspects that other articles don't go into.

      The optimisation tips are particularly interesting and not something you often see mentioned.

      Looking forward to future articles! Reply


  • BXTra

    Posted: February 19, 2011


    Good article. I like it. I just learn how to use NginX about a week ago and not much articles explain it clearly as what you wrote.

    However, when you made a comparation, you say nothing about Apache Worker MPM + PHP-FPM at all. (And many of articles I have read are usually compare to Prefork MPM.) From my personal experience, I feel that Nginx doesn't win anything much in term of performance when compare to Apache Worker MPM + PHP-FPM. In fact, I think they are almost exactly the same with one thing different. Apache Worker MPM uses more memory than NginX. (About 3 times) I also have no benchmark to prove about performance. Just my personal feeling when I tried to move from Apache Worker MPM to NginX hoping that it will reduce my server load :(

    Right now, I still don't know which one should I use, NginX alone, Apache Worker MPM alone or NginX as a reverse proxy together with Apache Worker MPM. Reply


    • fjordvald

      Posted: April 1, 2011


      Sorry for the late response. The reason I don't talk about the worker MPM is because this is a threaded setup and PHP is not guaranteed to be thread safe. This essentially means you're forced to use fastcgi for PHP which means you might as well just use Nginx as all the PHP advantages are now gone and Nginx is still faster than Apache worker MPM. Reply


      • Dave

        Posted: November 22, 2011


        I've been using php threaded for years, most of it is in fact quite thread safe, even if it's not *all* *guaranteed* thread safe. If you've been going for optimization, then surely you've tried it, with the minimum features you need compiled in, and seen if it worked for your app or not.

        Since people who are moving from apache to nginx are surely doing it for optimization's sake, then surely many have already tried optimizing the heck out of their apache server first since it's familiar to them, right? Therefore it's only fair to compare with worker MPM as well. Not solely worker of course, because not everyone can take that route, but many can. It just looks like Nginx fanbois trying to make Apache look bad when they all refuse to compare with worker, it turns apache guys off, come on, you want to win them over, right? Reply


        • fjordvald

          Posted: November 22, 2011


          No, I don't want to "win them over". I don't work for Nginx nor have any financial interest in it, my interest in Nginx is solely for my own gain, meanwhile I just document what I learn. So no, I don't really care whether or not people switch over.

          As for not comparing to the threaded MPM. You are correct that PHP might be thread safe but so long as it's not guaranteed I see no reason to actually use it, suddenly you want to use a feature that's not thread safe and you'll have to move from threaded MPM to worker MPM. In addition to not being thread safe, I'm also not comparing nginx + PHP to Apache threaded + PHP threaded because PHP used with ngix is not threaded, as such, comparing with threaded PHP you're actually not doing a one-to-one comparison.

          Also while your argumentation that people moving from Apache to nginx would have optimized Apache first is logically sound, the reality is sadly *not* logically sound. A lot of people come from shared hosting and just got their first VPS. I understand that you want to get some information that matches your scenario, but quite frankly your scenario is both unusual and not necessarily safe without extensive testing, if you really need threaded MPM then you surely have the knowledge to figure out if Nginx will help you.

          What I don't understand is your animosity against nginx, I hope you realize that by calling people nginx "fanbois" you're just coming off as a grumpy Apache "fanboi". Reply


  • great start

    Posted: May 22, 2011


    I love how you made things simple.

    I came from shared hosting and I am willing to use nginx.

    Thank you this article. Reply


  • Danilo

    Posted: May 24, 2011


    Great article, thanks. I'm currently moving from Apache to Nginx for memory reasons, so this helped me a lot. Flattr'd. Reply


    • fjordvald

      Posted: May 24, 2011


      Glad you liked it! Reply


  • Pkiula

    Posted: June 3, 2011


    The IF conditions, as well as the REWRITE conditions, in Nginx will usually make performance just as bad as Apache, if not worse. I had a server configuration with absolutely nothing but just one Rewrite. That's it. Inside "location / { }" block. Performance much more miserable than the Apache equivalent.

    Nginx is a great server, but when you can keep things utterly simple. Reply


    • fjordvald

      Posted: June 5, 2011


      Sorry, but this couldn't be any more wrong. I'm not sure how on earth you tested it but you definitely made a huge mistake somewhere. Reply


  • Baylink

    Posted: November 15, 2011


    > As of PHP 5.3.3 PHP-FPM is now part of the PHP core, which means you don’t have to patch the source code yourself, but can just set a compile time configure flag. Naturally, most distribution repositories have a PHP build with PHP-FPM enabled as well. So really, this disadvantage is barely a disadvantage any more as PHP-FPM is a very cool thing in itself. More on that another day.

    Did tomorrow ever come? :-)

    I'm about to implement a fairly large pre-written PHP app suite over PHP-FPM and nginx (we have it working on Apache, but the memory issues will make that a non-starter for production); any writeups I can read will be no more than enough. Reply


    • fjordvald

      Posted: November 15, 2011


      That another day never did come no, I did not intend to imply I would actually write such a post, just that it was too much to handle in one blog post. The reason I haven't written it is because I'd need benchmarks to back up my claims and benchmarks are notoriously hard to do for them to be valid. The work required to back up my claims in a way that would satisfy tech people would be too much for me to bother when I think it a non-issue compared to whether or not you'd want to maintain Apache next to Nginx. (The answer is no, btw, I would never, ever want to maintain Apache :P) Reply


  • Nathan

    Posted: January 20, 2012


    Very informative article, thank you!

    Since it is almost a year old I gotta ask; has the situation concerning shared hosting changed at all? Are they planning anything? I'm talking about a real alternative to .htaccess files. It's not a deal breaker to me though it would certainly help if nginx offered a solution.

    Thanks Reply


  • Joel

    Posted: March 6, 2012


    Quick question on the comparison of Apache MO versus Nginx MO (regarding that apache waits for the php script to finish before returning to listening): In apache the main httpd process's MPM will have several children that it pushes requests to as it accepts connections from the network. How is that approach different than the the nginx process? My understanding is the nginx still has child workers but after reading this I'm just wondering: does the event driven nature mean that a single child worker can handle multiple requests? Does threading allow for the concurrent requests? Reply


    • fjordvald

      Posted: March 7, 2012


      A single nginx worker handles multiple connections yes, nginx can run multiple workers but it's not required. People typically do it because workers are cheap, there's often very little to no performance gain from actually running multiple workers unless you have a ton of traffic.

      Apache 2.4 has event based MPM and Apache 2.2 as threaded MPM, these both handle multiple connections per child worker. The problem here is that PHP cannot safely be embedded into either of these MPMs as PHP is not event based and not guaranteed to be thread safe. Which means at that point you're actually running PHP separately through fastcgi so your setup is the exact same as with nginx, only slower. The only advantage I can really think of there is if you rely on .htaccess you can still do that with Apache as nginx doesn't support that. (I'd argue for good reasons too)

      So to sum up, if you use Apache then the prefork MPM makes the most sense so you can keep using mod_php. If you do not run prefork MPM then you might as well run Nginx as it's more lightweight and far easier to configure. The only reason to stay on Apache when not using prefork MPM is if you rely on .htaccess and don't want to redo it in nginx format. Reply


  • Joel

    Posted: March 6, 2012


    Basically, I'm seeking verification of my understanding child processes serving multiple requests is correct as well as interested to know the "mechanical" differences between apache mpm and "event-based" request handling. Reply


  • Andy

    Posted: September 1, 2012


    I'm sold. Trying to run a PHP webserver handling 200-300 simoutaneous connections on apache seems to need 8 Gigs of RAM. Thanks for the tech details, I can see now why people are getting the speed out of it, especially behind varnish using apc. 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=""> <strike> <strong>