WordPress Optimization and Monitoring

I spent some time recently working on improving the performance of a WordPress installation.  I had set up a new server at Digital Ocean, a relative newcomer to the virtual server world. In general, I’ve been pleased with their product. The pricing is good, the interface is easy to use and intuitive and the uptime has been good.

The default install for WordPress has been to use the Apache webserver. WordPress comes with the .htaccess rewrite rules for making nice looking urls using Apache.  Unfortunately, Apache doesn’t come configured out of the box with reasonable memory usage parameters and can quickly suck up as much RAM as you throw at it.

Each Apache server process was using about 35M of RAM.  On a 512M virtual server, I’m going to allocate about 350M or about 65% of memory to the webserver.  The configuration looks like this:

# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves

    StartServers          5
    MinSpareServers       5
    StartServers          5
    MinSpareServers       5
    MaxSpareServers      10
    MaxClients           10
    MaxRequestsPerChild   0

This keeps things under control, but performance was still not great. Load testing using ApacheBench with 10 concurrent requests showed an average response time just over 2500ms.

ab -n 100 -c 10 http://healthloop.com/index.php

I’ve used Nginx with a lot of sites recently and thought I’d see if it helped with performance here. Configuring Nginx with WordPress isn’t too complicated, but is less widely known than the Apache configuration:

server {
        listen 80;
        server_name example.com;

        root /var/www/wordpress;
        index index.php index.html index.htm;

        access_log /var/log/nginx/example.com.access.log;
        error_log /var/log/nginx/example.com.error.log;

        # Use pretty permalinks.
        location / {
            try_files $uri $uri/ /index.php?q=$uri&$args;

        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;

        location = /50x.html {
            root /usr/share/nginx/www;

        # pass the PHP scripts to php5-fpm
        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            include fastcgi_params;

        # Set Expire for static assets.
        location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
           expires 365d;


Unfortunately, I didn’t see any performance gains from switching to Nginx. I did have reduced RAM consumption, but testing showed an average of several hundred ms slower performance.

Enter Batcache and the APC Object Cache.  Batcache is a full-page caching plugin for WordPress and will cache the content of the WordPress site for anonymous users.  Authenticated visitors see the non-cached version, so this might not be the ideal solution for every WordPress site, but it was perfect for this scenario.  After installing Batcache and the WordPress plugin for APC support, testing showed the average response time had dropped to about 600ms per request.

Here’s the APC configuration I added to /etc/php5/fpm/php.ini. Initially, I had the shm_size at 32M, but noticed that the APC cache was getting highly fragmented. Since doubling the cache size, fragmentation has stayed low, in the 2-3% range.


;size per WordPress install

I’ve also been experimenting with monitoring both server and application status with New Relic. New Relic provides nice charts displaying application response time, CPU and RAM usage, and a number of other useful metrics. They also provide configurable notifications. Soon after installing the New Relic agent, I got an alert of high activity, checked the log file, and discovered an attack on /wp-login.php. Thwarted with iptables:

sudo /sbin/iptables -I INPUT -s -j DROP

Here’s an example of their rather elegant charts:

Screen Shot 2013-12-20 at 9.02.37 PMAnother option I’ve been exploring recently is Cloudflare.com. They offer caching of static assets in their CDN at their free account level along with some basic threat protection. So far it seems to be working out well, though perhaps not with as drastic improvements as I saw in this case.

WordPress on OS X with Nginx, PHP, and mysql


Recently, I wanted to do some WordPress development on my Mac.  I’ve got Ubuntu installed on a virtual machine, but I decided to get the stack running on OS X.

I’ve been using Nginx for a while now as a web server, caching proxy, and load balancer for some Plone sites.   It’s been fantastic – fast, reliable, easy to configure – greatly simplifying my life as a sysadmin.  I was interested to see how it would work for serving wordpress.

If you haven’t checked out homebrew, you should.   No, it’s not beer, but a package manager for OS X.  Homebrew is, they claim and I agree, the easiest and most flexible way to install the UNIX tools Apple didn’t include with OS X.

First, install and start the mysql server:

brew install mysql
/usr/local/bin/mysqld_safe --datadir=/usr/local/Cellar/mysql/5.5.10/data

Installing PHP is a little more complicated, since there isn’t an official homebrew formula. Instead, grab this formula:

wget https://github.com/ampt/homebrew/raw/php/Library/Formula/php.rb
mv php.rb `brew --prefix`/Library/Formula

Then, build PHP with mysql and fastcgi support:

brew install php --with-mysql --with-fpm

Tell PHP to listen on port 9000:

/usr/local/Cellar/php/5.3.6/bin/php-cgi -b 9000

Installing nginx is as simple as:

brew install nginx

You’ll need to modify the nginx.conf, which can be found in /usr/local/etc/nginx, and add this configuration:

server {
    listen       8080;
    server_name  localhost;

    location / {
        root   /path/to/wordpress;
        index  index.php index.html index.htm;

    location ~ .php {
    include fastcgi_params;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /path/to/wordpress/$fastcgi_script_name;

Start the Nginx server like this:

/usr/local/sbin/nginx -c /usr/local/etc/nginx/nginx.conf

There are more robust ways to start these services, but since this is just a development environment, I prefer not to have them running unless I am actively working with them. Now you should be ready to install WordPress.  Happy developing!