Skip to main content

Page caching with Surge and Cloudflare

In this lesson you will learn about the Surge page caching plugin for WordPress. You will install and configure it, and use the Surge events system to integrate with your Cloudflare zone to serve cached requests directly from the edge. You will also add the cache status to your Nginx logs for cache analysis.

Note this lesson covers the Surge page caching plugin for WordPress. If you're looking for an alternative, check out the Batcache lesson!

Surge

Surge is a very simple and lightweight page caching plugin for WordPress. It stores cached requests as PHP files on disk, leveraging the Linux kernel page cache as well as PHP's Opcache.

Given these storage requirements, Surge can't be decoupled from the server, and thus isn't suitable for multi-server environments or use as a separate cache server. However, for single-server environments, Surge is a great fit.

You can install and activate Surge using WP-CLI:

wp plugin install surge
wp plugin activate surge

The plugin doesn't require any additional configuration, as the defaults work well for most cases. If you do need to alter the configuration, you can do so with a custom config file and the WP_CACHE_CONFIG constant.

Surge will start caching pages immediately after activation. You can flush the cache using the plugin's WP-CLI command:

wp surge flush

The plugin will issue an X-Cache header with the status of the cache, so you can easily verify that it's working using curl:

curl -sI https://uncached.org | grep -i x-cache
# x-cache: hit

Surge maintains a list of popular marketing query parameters (such as utm_source) that are excluded when evaluating the uniqueness of a request. Request cookies starting with an underscore _ are also excluded. This behavior can be further customized by using your own Surge config.

The plugin uses flags to control cache invalidation. These are already implemented for core data structures as well as some WooCommerce components. Through a custom configuration, these can also be extended to modify Surge's invalidation behavior.

Cloudflare integration

One of the latest additions to Surge is the events system, which allows you to define custom code that runs on every single request.

By default, Cloudflare will not cache non-static HTTP requests. However, by using a custom event along with some zone cache configuration, we can instruct Cloudflare to cache our requests, effectively implementing edge caching with Surge.

These steps are entirely optional, but as you'll see in the upcoming benchmarks, they make a huge difference, especially if your audience is scattered across different regions.

We'll need three things:

  1. Ensure a correct Cache-Control header is being sent from Surge.
  2. Make sure Nginx serves the correct Cache-Control header for static assets.
  3. Change our Cloudflare zone settings to respect the Cache-Control headers.

Surge

Let's create a new Surge configuration file. I'll place mine in /sites/uncached.org/public_html/surge-config.php with the following contents:

<?php
$request_callback = function( $args ) {
    $status = $args['status'] ?? null;
    if ( ! in_array( $status, [ 'hit', 'miss', 'expired' ] ) ) {
        return;
    }

    header( 'Cache-Control: public, max-age=60, s-maxage=60, stale-while-revalidate=30' );
};

return [
    'events' => [
        'request' => [ $request_callback ],
    ],
];

Here we're allowing Cloudflare to cache the request for 60 seconds if the cache status is hit, miss, or expired. We also allow it to serve stale requests for up to 30 seconds while revalidating its cache, preventing cache stampedes on high-traffic sites.

You can opt for a longer TTL, but if you do, you'll need to set up proper invalidation via the Cloudflare API. This can be done with the expire event in Surge, though that implementation is beyond the scope of this lesson.

Next, we'll need to enable this Surge config in our wp-config.php file:

define( 'WP_CACHE_CONFIG', __DIR__ . '/surge-config.php' );

This constant instructs Surge to load our configuration file when initializing its own config.

Nginx

The default Nginx configuration doesn't usually send any Cache-Control headers, but because we are going to set Cloudflare to respect our headers, we need to make sure these headers are accurate for static assets:

This can be done using the expires directive with a map. Let's update our main /config/nginx/nginx.conf file and set default expires values for various content types inside the http context:

http {
    # ...

    map $sent_http_content_type $expires {
        default                 off;
        ~text/css               max;
        ~application/javascript max;
        ~image/                 max;
        ~font/                  max;
    }

    expires $expires;
}

Note that we intentionally left out text/html and application/json content types, since we'll provide cache headers for those manually through the Surge plugin events. If Surge is deactivated for any reason, those content types will remain uncached—which is the desired behavior.

Don't forget to reload your Nginx configuration:

sudo systemctl reload nginx.service

Now that the cache headers are in place, let's update our Cloudflare configuration to respect them.

Cloudflare

By default, Cloudflare caches only static assets, and text/html responses are always served directly from the origin. We can change this behavior with a new Cache Rule that respects origin Cache-Control headers instead of relying on Cloudflare's defaults.

This article is for premium members only. One-time payment of $96 unlocks lifetime access to all existing and future content on wpshell.com, and many other perks.