A page cache stores the finished HTML of a page so the next visitor gets it without WordPress building it again. The catch is the word “next”. Somebody has to be first, and on a quiet site that is a real visitor waiting, which I wrote about in why the first visitor after a cache clear waits.

ABCode Cache Warmer is what I built so the first visitor is my plugin rather than a customer. It requests your pages after the cache is emptied, and reads the response headers back so you can see whether each page really got stored.

By the end you will know whether you need it, what to tick and how to read the results.

Before you start

You need an administrator account, WordPress 5.6 or later and PHP 7.4 or later.

You also need a page cache. The warmer does not cache anything itself, it requests pages so that something else stores them, so without a cache in front of the site warming only adds requests. To check, look at your homepage’s response headers:

# Fetch headers only, and pull out the ones caches use.
# Run it twice: the first request may legitimately be a miss.
curl -sSI https://example.com/ | grep -i -E 'x-cache|cf-cache-status|x-litespeed-cache|x-fastcgi-cache|age:'

A HIT, or an Age: header counting up, means something is caching your pages. Nothing at all means there is no page cache, and that is what to sort out first. WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache and host-level caches all qualify, and the warmer works alongside them.

One more judgement. On a site busy enough that pages are requested constantly, the cache stays warm by itself. This earns its place on quiet sites and sites that purge often, which I went through in cache preloading in WordPress, and when it helps.

Step 1: Install it and open Tools, then Cache Warmer

Plugins, then Add New, search for “ABCode Cache Warmer”, Install Now, Activate. Or download the zip from the wordpress.org listing and use Upload Plugin if your host blocks direct installs.

The screen appears under Tools, with four tabs: Warmer, Results, Schedule and Advanced. Warmer is where you start. It shows a row of stats (In Queue, Warmed, Already Cached, Last Warmed), the “What to warm” checkboxes, and the button that starts a run.

The Cache Warmer screen under Tools on a fresh test install, showing the Warmer, Results, Schedule and Advanced tabs, the What to warm checkboxes, a summary of the last run, and a Warm Cache (5 URLs) button

The numbers there are small because it is a clean test install with almost no content, which is why the button offers five URLs. On a real site it shows hundreds or thousands, and the first job is checking that count is one you meant.

Step 2: Choose what to warm

The checkboxes are Homepage, Posts, Pages, Categories, Tags, Post Type Archives, Images On Warmed Pages and XML Sitemap. Each one you tick adds its URLs to the count on the button, so tick and watch the number move.

Start narrow. Homepage, Pages and Posts covers most sites, and the homepage is where a cold load costs you most. Categories and Tags matter if people navigate by them and are pointless if nobody visits those archives. Post Type Archives is for custom post types. XML Sitemap warms from your sitemap instead, reading core, Yoast and Rank Math sitemaps, and it is the widest option.

Images On Warmed Pages needs understanding first. Static image files are served off disk without WordPress being involved, so requesting them achieves nothing. It is for sites where images are generated on demand, which is how ShortPixel, Optimole, EWWW and Jetpack’s image handling work. Without one of those, leave it off. Whatever you pick, the plugin orders the queue: homepage first, then archives, then content.

Step 3: Run a first warm and read the queue

Press the button. In Queue climbs, then falls as URLs are processed, and Warmed and Already Cached fill in behind it. Runs continue through WP-Cron in the background, so the tab need not stay open.

Every URL comes back as one of four outcomes.

  • Warmed: the page was not cached, and now it is.
  • Cached: it was already warm.
  • No cache: the page is deliberately not cacheable. Carts and checkouts land here, correctly.
  • Failed: the request did not succeed, with the actual error rather than a shrug.

A first run on a cold site is mostly Warmed. Run it again and it should be almost entirely Cached, which is the quickest proof your caching layer stores what the warmer sends it.

Step 4: The Results tab

Results keeps a history of runs: when each ran, what triggered it, how it went. Expand one and you get every URL, what happened to it, which cache layer answered and when. Retention is an age in days plus a maximum number of runs.

The cache layer column is what I use for diagnosis, because the plugin reads the response headers and reports which system replied. It recognises Cloudflare, LiteSpeed, Varnish, Fastly, Nginx FastCGI, Kinsta, SiteGround, 20i StackCache, WP Rocket and W3 Total Cache among others. When a page you expected to be cached comes back as No cache, that column says why: nothing answered, or the layer that did has been told to exclude that URL.

Step 5: Schedule, or warm on purge

The Schedule tab does fixed times: hourly, twice daily, daily at a time you choose, or weekly on a chosen day. Times are in your site’s timezone, and it defaults to 3am, when a crawl is least in the way.

The other trigger is a purge, and it is the better one. The plugin listens for cache clears from the common caching plugins, including WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest Cache, Cache Enabler, SiteGround Optimizer, Nginx Helper and 20i StackCache. A schedule cannot know when your cache was emptied, and a 3am crawl does nothing for an 11am purge.

Purge-triggered warming has no quiet hours, deliberately, because a purge means the cache is cold now. It folds a burst of purges into one run rather than a crawl per post, which stops a bulk edit becoming a stampede. Publishing one post warms that post plus the pages listing it.

If your caching plugin is not on that list, a filter adds its purge action. That belongs in a small site-specific plugin, not functions.php, which a theme update wipes:

// Tell the warmer about a purge action it does not already know.
add_filter( 'abcw_purge_hooks', function ( $hooks ) {
	$hooks[] = 'my_cache_cleared'; // the action your cache fires on a purge
	return $hooks;
} );

Step 6: The Advanced tab, and why to leave it alone

Every warming tool has one setting people get wrong: how hard to crawl. Too gentle and the run never finishes, too aggressive and you have pointed a load test at your own server. So the plugin decides instead of asking. It measures your response time, combines that with the PHP memory limit, CPU count and current server load to pick its concurrency and delay, then backs off if the server struggles mid-run. If your host answers 429 or 503, refused URLs are requeued and the crawl slows to one at a time.

Advanced overrides those numbers by hand, along with SSL verification and retention. SSL verification is off by default so staging sites with self-signed certificates work, and you can turn it on for a live site. Leave the tuning alone unless runs are genuinely too slow, and even then a narrower scope is the better fix.

What it cannot do

It cannot warm pages that are not cacheable. Carts, checkouts, account pages and anything shown only to logged-in users are excluded from caching for good reasons. The plugin reports them honestly as No cache, and on WooCommerce you should skip /cart/, /checkout/ and /my-account/* by pattern.

It does not make a warm page faster. Once cached, a page is as fast as your caching layer can serve it, so warming only affects the first request after it goes cold. Nor does it reduce total work: every cold page is built once by somebody, and warming decides that somebody is a background request.

On a busy site whose cache never goes cold it is unnecessary. And if your uncached render is slow enough that warming feels essential, the render is the real problem, which is speed optimisation work rather than a caching setting.

How to check it worked

Run a warm, wait for the queue to empty, then run it again. The second run should come back almost entirely Cached. If it comes back Warmed again, the pages are not being stored, and the problem is in the caching layer. Confirm by hand too: clear the cache, warm, then request a page with curl -sSI and look for a HIT.

When it does not work

Every URL comes back as No cache

Nothing is caching your pages. Either there is no page cache, or a plugin or your login state is bypassing it. Test with curl while logged out, since most caching setups deliberately skip logged-in users.

Runs start but never finish

WP-Cron is the usual cause. WordPress’s scheduler only fires when someone visits, so a quiet site can leave a run stalled between visits. A real server cron calling wp-cron.php fixes it, and most hosts offer that in their panel.

Pages are warmed and go cold again minutes later

Something is purging constantly, or the cache lifetime is very short. Find that trigger before warming harder, because a plugin clearing everything on every save empties the cache faster than anything can fill it. I covered that in a cache that will not clear.

The run is slowing the site down

Narrow the scope first: untick Tags, Categories and Images On Warmed Pages. If that does not settle it, lower the concurrency in Advanced and move scheduled runs to a quieter hour.

Common questions

Do I still need my caching plugin?

Yes. This does not cache anything. It fills the cache your caching plugin creates, and without one there is nothing to fill.

Will it slow my site down while it runs?

It adds load equal to its concurrency setting, so five concurrent requests is roughly five extra visitors. Re-warming already warm pages costs a fraction of the first crawl, because cached pages never reach PHP.

Will it inflate my analytics?

Not Google Analytics, Matomo, Plausible or Fathom, because those count a visit when their script runs in a browser and the warmer never executes JavaScript. Server-side tools do see the requests, but they carry DNT: 1 and X-ABCode-Cache-Warmer headers so you can filter them out.

Does it work with WooCommerce and multisite?

Yes to both. On WooCommerce, warm the shop page, product archives and products, and skip cart, checkout and account pages by pattern. On multisite each site is treated independently, with its own settings and runs.

Getting it running and leaving it alone

The setup that suits most sites is small: Homepage, Pages and Posts ticked, purge-triggered warming on, Advanced left as the plugin measured it. Add Categories or the sitemap if your traffic goes there, check Results weekly for a fortnight, then stop thinking about it.

What you end up with is a site where publishing a post or clearing the cache no longer hands the rebuild to whoever visits next. That is a real benefit on a quiet site and close to nothing on a busy one, which is why I would rather you checked first. The plugin has its own page on this site, and it is free with no URL limits. If the numbers still look wrong once it is running, get in touch.