Pagespeed: Nginx + Memcached + PHP5-FPM

Nginx with PHP5-FPM is already quite fast, if finished pages are saved using Memcached, the loading time can be reduced even further. The implementation is simple.

This article is inspired by a manual on 6tech.org and transferred to current software versions. The following requirements must be met for Nginx setup to benefit from Memcached:

  1. nginx with PHP5-FPM
  2. php5-memcached modules installed and activated
  3. memcached must be installed

The basic idea: instead of the actual index.php an intermediate script is called, which passes the call to the actual index.php and stores the result in the memcache. So you get a caching solution similar to the plugin WP Supercachewhere the finished pages are stored. In case of a cache hit only one PHP call is necessary to deliver the memcache result - since the data is stored in RAM this is much faster than a regular call.

index.php replacement

The following PHP script must be placed in the directory of the WordPress to be cached, for example as index-memcached.php.

addServer('127.0.0.1', 11211);

// Cache time in seconds (1 day)
$cacheTime = 60 * 60 * 24 * 1;
$cacheKey = "fullpage:{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";

$debugMessage = 'Page retrieved from cache in %f seconds';
$html = $memcached->get($cacheKey);

if ( ! $html) {
    $debugMessage = 'Page generated in %f seconds';

    ob_start();

    require 'index.php';
    $html = ob_get_contents();

    $memcached->set($cacheKey, $html, $cacheTime);

    ob_end_clean();
}

$finish = microtime(true);

echo $html;
echo '';
exit;

The caching duration must be specified in seconds, the formula gives a caching of one day. So once a page has landed in the Memcache, it will be updated after one day. This should be considered when it comes to comments, for example: if you use the WordPress comment function, the contributions of readers would not be updated. Due to the here used Disqus-Comment-System with Lazyload this problem is avoided.

Nginx configuration

In order for Nginx to forward the requests to the new index-memcached.php and to disregard the previous index.php at least for guests, the server configuration must be adjusted. This is done in the VirtualHost configuration of the respective WordPress installation. At server {...}-Add the following code to the section:

#caching
set $cache_flags "";

    # Is this a mobile browser?
    if ($http_user_agent ~* "(2.0 MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mob
        set $cache_flags "{$cache_flags}M";
    }

    # Is the user logged in?
    if ($http_cookie ~* "(comment_author_|wordpress_logged_in_|wp-postpass_)" ) {
        set $cache_flags "${cache_flags}C";
    }

    # Do we have query arguments?
    if ($is_args) {
        set $cache_flags "${cache_flags}Q";
    }

    # If none of the rules above were matched, we can use the cache
    if ($cache_flags = "") {
        set $index_file /index-memcached.php; #must set to index-memcached.php for using cache
    }

    # Otherwise we'll just use index.php
    if ($cache_flags != "") {
        set $index_file /index.php;
    }

    location / {
        index $index_file index.html;
        try_files $uri $uri/ $index_file?$args;
    }

    # Force /wp-admin requests to always use index.php
    location /wp-admin/ {
        index index.php;
        try_files $uri $uri/ /index.php?$args;
    }

How it works: only if a desktop user calls up the page and not the dashboard, the corresponding page is delivered from the cache. If a Responsive page uses the same code for both desktop and mobile devices, you can comment out the corresponding section.

Conclusion

What has the effort been worth now? A test with webpagetest.org brings the following Result: the time-to-first-byte is 0.092s, the time until the completely loaded website is 0.993s. This corresponds to a speed index of 600.

The problem with this solution is that minify using W3 Total Cache does not work. To enable defer and async for Javascript, this has to be stored directly in the corresponding WordPress file.

Leave a Reply

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