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.

Using WordPress in multiple environments


When developing software, it’s important to have different environments so that modifications made by developers don’t affect others outside their own “sandbox” or the “production” server. Best practices generally dictate three tiers: a local development environment for each developer, a staging environment to integrate changes from multiple developers, and a production environment.

WordPress is one of the best blogging platforms available, but it isn’t really designed to be used in multiple environments. It isn’t too difficult to setup multiple environments, though. Configuration variables can be read in from environment variables that tell WordPress which environment it is in.

With Apache, add the environment variables to your .htaccess file:

SetEnv WP_DB your_database_name
SetEnv WP_USERNAME your_username
SetEnv WP_PASSWORD your_password

Then you can grab these settings in the WordPress configuration file, wp-config.php:

// The name of the database for WordPress
define('DB_NAME', $_ENV['WP_DB']);
// MySQL database username
define('DB_USER', $_ENV['WP_USERNAME']);
// MySQL database password
// For developers: WordPress debugging
define('WP_DEBUG', $_ENV['WP_DEBUG']);
// Override the wp_options and set the site
define('WP_SITEURL', 'http://' . $_SERVER['SERVER_NAME']);
define('WP_HOME', 'http://' . $_SERVER['SERVER_NAME']);

You can also set these environment variables using nginx:

include fastcgi_params;
fastcgi_param WP_DB your_database;
fastcgi_param WP_USERNAME your_username;
fastcgi_param WP_PASSWORD your_password;

You probably also need to tell php to load your environment variables in php.ini:
variables_order = "EGPCS"

Now you can have multiple environments set up to use with your version control without any additional direct configuration of WordPress.