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.

[APC]
extension=apc.so
apc.enabled=1
apc.shm_segments=1

;size per WordPress install
apc.shm_size=64M

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