Apache vs. Nginx: Pros & Cons for WordPress

WordPress performance requirements can vary amongst projects, but one thing certainly remains the same throughout—it must be fast

Another requirement that goes without saying—it must be economical, so we cannot have resource intensive solutions. Solutions must be lean, mean and reliable, and to fully maximise income on your site, the expenditure on hosting must be kept to a minimum.

If you are expecting to be receiving a lot of traffic, the web server configuration you’re serving WordPress on has a direct effect on the performance of your site, affecting the load time and stability. 

When choosing your web server, you have several choices; Apache, Nginx, IIS, Caddy, and Lighttpd are all popular projects. We will be covering Apache and Nginx in this guide.

It’s estimated that out of the whole internet combined, Apache Server and Nginx together serve 50% of all web traffic. 

If you are a newcomer to the topic, maybe you are confused by the two seemingly identical purposes of the software—to serve web sites. I hope to clarify more below how the two differ and how to take advantage of either’s features. 

Apache and Nginx are very established projects, and they both have their own reasons for being so whilst achieving a similar identical goal of serving your WordPress site. However, when we look deeper in their designs, there is a major difference in how connections are handled by each server. 

The Difference

A major difference is in how connections are handled by the two.

Put simply, Apache uses a forked threaded solution, or keep-alive, which keeps a connection open to each user.

On the other hand, Nginx uses a non-blocking event loop, which pools connections working asynchronously via worker processes.

Nginx worker process topology diagram

Because of this architecture, the result is a single-threaded nginx process, and additional processes are not spawned to handle each new connection. So even at times of a high load, the CPU and RAM don’t get caned in this approach.

As well as architecture, there are also some slight differences and nuances between the two in configuration, and we will go through these in more detail later in this guide. 

First, let’s look at the two projects and get a clear overview.

Apache

WordPress works with Apache pretty much straight out of the box, A PHP module such as mod_php is required, but there is not much else needed to get it going. 

Apache is very flexible and has a bunch of modules. Commonly, mod_rewrite is used to provide URL rewriting to interpret URLs such as categories.php?id=4 to /categories/4.

Nginx

  • Started in 2002 by Igor Sysoev, a Russian software engineer, as a solution to the C10k problem—a challenge for web servers to handle 10,000 concurrent connections. 
  • Released publicly in 2004, meeting the goal as an asynchronous, non-blocking, event-driven architecture.
  • Lightweight running on minimal hardware provides excellent static content performance.
  • Highly responsive under heavy load.
  • Uses an nginx.conf configuration file with a curly bracket JS-like syntax.
  • Extensible with third-party modules.

As a successor to Apache, Nginx has the benefit of knowing the pitfalls and performance issues of concurrency that can occur, and it reaps the full rewards of this with its very fast asynchronous event loop design.

For static content this works very fast. As for dynamic content, like PHP for example, Nginx does not have the ability to process this with a module as Apache does. But this is not a hindrance, as it uses FastCGI to achieve this. This works very well in conjunction with php fpm connection pools and memcache.

WordPress Requirements

  • PHP 7 > 
  • MySQL 5.6 or Maria DB 10.0 >
  • mod_rewrite (if using Apache)
  • SSL (if being used)

Both Apache and Nginx support php fpm. This is a FastCGI manager, a forked process manager for PHP which can be used to provide a very fast response time. Running as a daemon on the server, it will natively spawn processes as they are required. 

Configuring PHP FPM With Apache

Ubuntu and Debian users can install the required packages with aptitude via:

sudo apt-get -y install libapache2-mod-fastcgi php7.0-fpm php7.0

Now enable the module in apache:

a2enmod actions fastcgi alias

Then, in the configuration file /etc/apache2/conf-available/php7.0-fpm.conf, add the following:

<IfModule mod_fastcgi.c>
    AddHandler php7-fcgi .php
    Action php7-fcgi /php7-fcgi
    Alias /php7-fcgi /usr/lib/cgi-bin/php7-fcgi
    FastCgiExternalServer /usr/lib/cgi-bin/php7-fcgi -socket /var/run/php/php7.0-fpm.sock -pass-header Authorization
</IfModule>

Also, in your VirtualHost for WordPress (default path /etc/apache2/sites-available/000-default.conf), add the following:

<Directory /usr/lib/cgi-bin>
    Require all granted
</Directory>
<IfModule mod_fastcgi.c>
    SetHandler php7-fcgi .php
    Action php7-fcgi /php7-fcgi virtual
    Alias /php7-fcgi /usr/lib/cgi-bin/php7-fcgi
    FastCgiExternalServer /usr/lib/cgi-bin/php7-fcgi -socket /var/run/php/php7.0-fpm.sock -pass-header Authorization
</IfModule>

Now restart apache and you’re good to go

systemctl restart apache2.service

Make a <?php phpinfo(); ?> file and look in your browser. PHP will now be serving with FPM. 

Now check your WordPress blog. Notice any difference?

Configuring PHP FPM With Nginx

Ubuntu and Debian users can install the package with the following:

apt-get -y install php7.0-fpm

Now, inside your configuration file (default /etc/nginx/sites-available/default) in the server block, you need to add the FastCGI configuration as follows:

server {
...
 location / {
    # use try files to try and serve the file
    try_files $uri $uri/ =404;
 }
 
 # PHP FPM Configuration
 location ~ \.php$ {
    include snippets/fastcgi-php.conf;

    # Connect via socket
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
 }
 
 # deny apache .htaccess requests
 location ~ /\.ht {
  deny all;
 }
 ...
 }

Here we use the snippet from Nginx to set the cgi parameters and pass fastcgi the socket connection. 

Next, make sure you set the cgi.fix_pathinfo=0 in the php ini, as the default setting is breaking the configuration. Edit /etc/php/7.0/fpm/php.ini and set:

[...]
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is.  For more information on PATH_INFO, see the cgi specs.  Setting
; this to 1 will cause PHP CGI to fix its paths to conform to the spec.  A setting
; of zero causes PHP to behave as before.  Default is 1.  You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
; http://php.net/cgi.fix-pathinfo
cgi.fix_pathinfo=0
[...]

Now you can save the file, and reload PHP FPM. Do this via:

service php7.0-fpm reload

Finally, we can check the <? phpinfo(); ?> in a browser to confirm the server is now using PHP FPM with Nginx.

Doing mod_rewrite in Nginx

Nginx does not use a .htaccess file, and for URL rewriting it has a much simpler approach.

To get your WordPress blog working with Nginx, simply add the following to the try_files part of your Nginx configuration:

location / {
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?q=$uri&$args;
}

If you are using a directory for your WordPress blog, please set the following:

location /wordpress/ {
    try_files $uri $uri/ /index.php?q=$uri&$args;
}

Restart Nginx and you will have URL rewriting working.

$ service nginx restart

Optimal Setups

You have many options to optimise WordPress via caching on the server via memcache, varnish and also at the WordPress app level with plugins which will easily let you access this. 

However, what Nginx gives you is a great solution to serving static website content with its robust and rapid static content cache.

Static Content Cache

Nginx is very fast when used as a static content cache, and this is where its usage really excels in terms of WordPress and blog posts with a lot of images. You can serve your CSS, JS and images all via an Nginx server running just for these needs. 

It’s best always to do this on a cookie-less domain so that the content will be truly cached by the browser (as cookie as not cachable), so using a subdomain such as images.myblog.com or static.myblog.com would be ideal.

A location block for this static subdomains configuration would look like this:

    location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|html|xml|otf|ttf|eot|woff|svg)$ {
        access_log off;
        expires 30d;
        tcp_nodelay off;
        ## Set the OS file cache.
        open_file_cache max=3000 inactive=120s;
        open_file_cache_valid 45s;
        open_file_cache_min_uses 2;
        open_file_cache_errors off;
    }

Using open_file_cache, we enable caching for our static media files. We specify the maximum files to cache and for how long with open_file_cache max=3000 inactive=120s;

if you want to set up caching project-wide, just add the following four lines in your nginx.conf configurations:

open_file_cache          max=10000 inactive=5m;
open_file_cache_valid    2m;
open_file_cache_min_uses 1;
open_file_cache_errors   on;

Important: The open_file_cache_errors will cache the actual 404 errors, so it’s better to turn this off if you are using a load balancer in conjunction with this.

PHP-FPM Connection Pools

It’s possible to use different pools for each different WordPress, and you can then allocate resources very accurately for each site—even using different users and groups for every pool if need be. The configuration is very flexible. 

You can set up several configurations, for example:

/etc/php-fpm.d/family-site.conf
/etc/php-fpm.d/travel-blog.conf
/etc/php-fpm.d/cooking-recipes.conf

In each of the following, we can set a plethora of configurations like so:

[site]
listen = 127.0.0.1:9000
user = user
group = websites
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-site.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

With this, you can specify the PHP-FPM configuration options such as pm.max_children, and you can also specify environmental variables and set the username and group settings here.

Nginx Load Balancer

If you are going to be getting a lot of traffic then you will probably want to set up a load balancer to use with your php-fpm setup.

Conventionally, we will want to start several back-end upstream servers, all of which are running mirrors of your blog, and then have another server running nginx in front of this which is acting as a load balancer and will direct the load between the upstreams. 

This means you can use many servers to power your blog at once, and the configuration to do so is relatively easy. 

An example configuration would look like this. First we start with an upstream module:

upstream backend  {
  server backend1.example.com;
  server backend2.example.com;
  server backend3.example.com;
}

Here, each backend1.example.com has its own Nginx configuration, a mirror of how the site was before it had a load balancer. Nginx will choose which server to use for each request.

If one of our back ends has a faster hard disk, like SSD for example, or is geographically closer to your main user base, you can set weighting like so:

upstream backend  {
  server backend1.example.com weight=1;
  server backend2.example.com weight=2;
  server backend3.example.com weight=4;
}

Additionally, if you think a server may go down or are concerned about timeouts, there are configuration options for this also:

upstream backend  {
  server backend1.example.com max_fails=3  fail_timeout=15s;
  server backend2.example.com weight=2;
  server backend3.example.com weight=4;

Now, with this configuration, after 3 failures or a 15-second timeout, the server will be no longer used by the load balancer. If you wish to manually mark a server as inactive, add the keyword down, e.g. server backend3.example.com down;.

Next we need to pass that to the server via a proxy using the backend upstream we just defined prior:

 server {
  location / {
    proxy_pass  http://backend;
  }
}

Now restart your server with service nginx restart, and you are running a load-balanced version of your site! 

Lastly on this topic, also for your reference here is an nginx guide on serving static content and the best configuration options. Take note of using tcp_nopush and sendfile for Mp3, for example.

Migrating Apache to Nginx

Besides reading the Nginx manual and having a go at doing the changes yourself, you can use the open-source tool apache2nginx to translate your configuration from Apache to Nginx.

Follow the installation steps at the apache2nginx README, and once installed you will have the ability to migrate configuration files simply by running:

$ apache2nginx -f /etc/httpd/conf/httpd.conf

You can now check the configuration and try it in your Nginx installation. You may find the translation was not perfect, but it will give you a base to start on.

Conclusion

For speed and performance, Nginx is the obvious choice over Apache, but that’s not to say Apache can’t handle some traffic. If you’re planning on going on the front page of Reddit any time soon, you should probably look at getting a heftier solution with Nginx and PHP-FPM.

Migrating your WordPress to Nginx is not very difficult, and configuration going forward in Nginx is very simple and easy to access in comparison with that of Apache. 

Although there are not the same modules as Apache, and it may be unfamiliar at first, you will be able to find a replacement in most cases. If not, as a fallback solution, you can always proxy the old server via your nginx for this purpose if need be.

There are many ways to configure both servers, so a good solution can almost always be found for whatever the requirements call for. For now, it seems Apache will always be the default choice on the widely available hosting software cPanel, due to the EasyApache setup tool that comes with it. 

In the future, maybe more hosts will adopt Nginx cPanel tools like Engintron that provide Nginx on cPanel also. 

For now, if you wish to switch over to an Nginx-powered WordPress, you will need to set up a Linux VPN at DigitalOcean, AWS, or another hosting provider.

Leave a Reply

Your email address will not be published. Required fields are marked *