I've been hearing a lot about a web server called Nginx recently. It's frequently used for Ruby on Rails web applications, and it's supposed to be small, fast, and lightweight. Apache's httpd (I'll refer to this simply as Apache), on the other hand, is the de facto standard for web servers in the Linux world, and it comes pre-installed on almost every server distribution. I decided last weekend to do some comparison tests and see how they each performed under the same conditions. First, I spun up a new EC2 server running CentOS 5.4. I chose the m1.small size. I logged in and disabled all of the unnecessary startup services like avahi-daemon and Bluetooth, and downloaded the latest version of WordPress. I made a very simple blog, using poems from Catullus as my content, and included a small jpeg in each blog post. I made 10 blog posts, enough to fill the landing page, which by default in WordPress installations will show the most recent 10 blog posts (I used MySQL as my database back end, for those of you who are curious). Finally, I configured both Apache and Nginx so that I could go to http://testblog.test.com and the WordPress site would load automatically. Now, I should mention that while Apache uses mod_php to process PHP requests, Nginx doesn't have any sort of CGI-handling capabilities built in. I had to install spawn-fcgi, and whenever I tested against Nginx, I would start both Nginx and spawn-fcgi. I tried to go for as "vanilla" a configuration as I could, so I chose to spawn 15 php-cgi processes, which seemed to be the consensus among the various guides I found online.
Once everything was set up and I had confirmed the site worked under both web servers, I shut off Nginx and spawn-fcgi and fired up Apache. I used the Apache Benchmark utility (ab) on my laptop to slam the web site with requests. It took a little bit of experimenting to find some good values, but I settled on 50 concurrent requests up to a total of 300. I saved the output and re-ran the test against Nginx. I ran 23 tests in total, playing around with various things to see what effect each would have on the overall performance. I tweaked the number of child processes spawned by Apache and by spawn-fcgi (very little difference), I modified my php.ini to get rid of extensions I didn't want and trim any fat I could find (again, this didn't make much difference), I installed the wp-super-cache plugin for WordPress (HUGE difference!!), and finally, I ran a number of tests using a very small, static HTML file instead of the WordPress blog. Here's a quick summary of the test results:
Across all of my tests, Nginx consistently performed better than Apache. Often the difference wasn't very large, and in one case Apache beat Nginx, but the overall trend is clear - Nginx is faster. It seems to be faster regardless of whether it's serving dynamic content or static content, or whether the blog had caching enabled or not. In addition, Nginx and spawn-fcgi only used 17 processes between them (2 for Nginx and 15 for spawn-fcgi), whereas Apache would routinely use its maximum allowed, 256. That didn't matter a whole lot as far as how fast requests were serviced, but it did make the load on the server appreciably higher. I broke the results down a little more, into two groups. Tests A - F were all very similar, in that I was testing the same number of requests at the same concurrency, and always against an uncached WordPress front page. So these two graphs show just those results, both the requests per second that each web server was able to process, and also the mean time per request (in ms). Both of these stats come straight out of the ab output.
Test A - Default configurations, WordPress, 300 total requests, 50 concurrent Test B - Slim php.ini, Wordpress, 300 total requests, 50 concurrent Test C - Tweaked # of client processes, WordPress, 300 total requests, 50 concurrent Test D - Default configurations, WordPress built-in caching enabled, 300 total requests, 50 concurrent Test E - Default configurations, WordPress built-in caching enabled, 300 total requests, 50 concurrent Test F - Default configurations, WordPress, 300 total requests, 50 concurrent The second group of tests is more dynamic. I added the wp-super-cache plugin, I started requesting static content, and I changed the number of overall requests as well as the number of concurrent requests.
Test G - Default configurations, WordPress with wp-super-cache, 200 total requests, 50 concurrent Test H - Default configurations, static HTML, 10000 total requests, 200 concurrent Test I - Default configurations, static HTML, 10000 total requests, 200 concurrent Test J - Default configurations, static HTML, 10000 total requests, 200 concurrent Test K - Default configurations, static HTML, 3000 total requests, 50 concurrent Obviously, this experiment could have been more scientific. For the results to be really meaningful, I should have repeated each test multiple times and I should have verified the results returned to document any errors in the process. I am also quite sure that both Apache and Nginx could be configured with better performance in mind, as could MySQL and WordPress. But this post isn't pretending to be a guide to configuring any of these applications - it's meant just as a rough comparison between Apache and Nginx under mostly default configurations. Analyzing the data from that perspective, there are two very clear data points that emerge. First, Nginx outperformed Apache on every test but one. Second, installing the wp-super-cache plugin in WordPress made an immediate and enormous difference in performance, and should probably be the first thing you consider when trying to improve the responsiveness of your blog.

Comments
My Snort IDS consistently warns about Nginx server strings, with the warning "often hostile traffic" or "likely hostile"; these are from the Emerging Threats rules. Seems as though Matt Jonkman has decided that the Bad Guys have latched onto Nginx for their own nefarious purposes. If enough Good Guys start using it too then this will become just another web server and these warnings will stop. In fact, I'm beginning to think these might all be considered false positives. (Sorry Matt.)
Hi Chris,
Interesting data.
How did you ensure that you weren't saturating the network connection between your laptop and the EC2 instance?
Dan