Black Friday: 50% off. Your first year on any annual plan, until 1 December. See the dealFirst year on annual plans, until 1 December
WPClientHub V2 is live. Fewer plugins on your client sites: caching, forms and image optimization now come built into WPClientHub One. See what’s newCaching, forms and images, built in. See what’s new
Sign in Start free

← Docs  /  Pulse performance

Caching

Updated 2026-10-08

A page cache on the client’s own server, every plan: what is cached and what never is, the drop-in, smart purge, preload, other cache plugins, nginx.

Browse documentation

What it does

Caching stores a finished copy of each page a site shows to visitors who are not logged in, and answers the next visitor from that copy before WordPress loads — no PHP theme rendering, no database queries. It runs on the client’s own server inside the WPClientHub One plugin; there is no CDN and nothing leaves the site. It is on every plan, Free included, and needs WPClientHub One 2.0 or newer on the site.

Three things come with it:

  • Smart purge — a page is removed from the cache the moment its content changes, and everything is purged after the changes that touch every page (theme, plugins, menus, widgets, the Customizer, core) and after what WPClientHub itself does to a site: an update batch, a restore or rollback, a database cleanup, an image run.
  • Preload — after a full purge the site warms its most important pages again in the background, a few at a time on its own cron, so visitors rarely meet a cold page.
  • Static-file rules on Apache and LiteSpeed: browser caching (Expires / Cache-Control) for CSS, JavaScript, images and fonts, and gzip or Brotli compression for text, in a marked block of the site’s root .htaccess.

If the site already has a cache plugin — or the host caches pages itself — WPClientHub’s own cache stays off and says who handles it; purging from the dashboard still works through that plugin, and still happens on its own after updates, restores, cleanups and image runs. See Other cache plugins and hosts.

Where to find it

Open a site, pick the Performance tab: the Caching card sits under the report header. The Overview tab’s Performance card carries one line about it (“Cache on · 90% served from cache”). The card shows:

  • The status — On, Switching on, Blocked on the site, Handled by plugin or host, Off, Plugin update needed, or Site not connected.
  • Served from cache — the share of page views the cache answered over the last seven days (hits ÷ hits + misses), with the counts; pages cached and their size on disk; when pages expire; and what serves them (the PHP drop-in or the web server).
  • Purge all and Purge URLs…, Settings, Check now (asks the site for its status), a preload in progress with a Stop button, the rules in effect, and the site’s last purges with their reasons.

Press Switch on caching (or turn it on in Settings) and the site installs its drop-in on the spot; Switching on means the settings are on their way and the site applies them on its next check-in.

What is cached, and what never is

Cached: whole HTML pages requested with GET (or HEAD) by a visitor who is not logged in, without a query string — tracking parameters are ignored by default (utm_*, gclid, fbclid, msclkid, dclid, mc_cid, mc_eid, _ga, _gl), so /pricing/?utm_source=newsletter gets the cached /pricing/. HTTP and HTTPS are cached apart. Pages are stored as a plain file and a gzip file under wp-content/cache/wpclienthub/ and expire after the time you set (10 hours by default).

Never cached, never answered from the cache:

  • Anyone logged in, anyone who unlocked a password-protected post, a commenter with remembered details, a WooCommerce or Easy Digital Downloads session or cart, a password reset — by their cookies — and any cookie you add to the bypass list.
  • The WooCommerce cart, checkout and account pages and the EDD checkout (found from the site’s own settings), and any path you exclude.
  • Password-protected and private posts, previews, the Customizer, search results, feeds, embeds, robots.txt, the admin, the login page, the REST API, XML-RPC, cron, and anything under wp-content or wp-includes.
  • POST requests, requests with an Authorization header or made by JavaScript (X-Requested-With), and any query string that is not on the ignore list.
  • Redirects, 4xx and 5xx responses, responses that are not HTML, empty or truncated pages, pages over 5 MB, pages that start a session, pages sent with Cache-Control: no-store or private, and any page where a plugin defined DONOTCACHEPAGE — the constant every WordPress cache plugin honours.

Phones and desktops get the same copy unless you switch on separate copies for phones — most responsive themes serve one HTML to both, so it is off by default.

How a cached page is answered

The PHP drop-in (default, everywhere). Switching caching on writes wp-content/advanced-cache.php — the drop-in WordPress itself loads very early, before plugins, the theme or a database connection — and adds define( 'WP_CACHE', true ); to wp-config.php when it is not already there. The drop-in looks for the page’s file and, when it exists and is fresh, sends it and stops; otherwise WordPress loads exactly as it would without it. It fails open: if the WPClientHub One plugin folder or the cache configuration is missing (the plugin was deleted by hand, say), it does nothing and the site works normally. The hit is marked with an X-WPCH-Cache: HIT header; a page WordPress rendered carries MISS.

The web server (Apache and LiteSpeed, optional). Pick Web server in Settings and the plugin adds rewrite rules to the site’s root .htaccess — before WordPress’s own block — that answer a cached page without starting PHP at all: a GET with no query string, no login, session or bypass cookie and no Authorization header, on a path that is not excluded, when the file exists. The plugin then checks itself: it stores a probe page, requests it from the site, and looks for the probe and the X-WPCH-Cache: HIT (server) header. If the check fails (mod_rewrite off, AllowOverride None, a proxy in front) the page rules come out again and the PHP drop-in keeps serving; the card says why. The browser-caching and compression rules stay either way. The web-server path needs HTTPS and is not used with separate phone copies (the server cannot tell phones apart).

nginx. nginx does not read .htaccess, so the PHP drop-in serves pages. The card offers a snippet for the server block that does three things: compression and browser caching for static files, and a try_files that answers cached pages straight from the cache folder, with the same cookie and query-string rules. Paste it, reload nginx, and the drop-in simply has nothing left to do.

Purging

On the site, as content changes (one purge per request, at the end of it):

ChangeWhat is purged
A post or page published, updated, unpublished, trashed or deleted; a comment approved, edited or its status changedIts page (and its old address when the slug changed), the front page, the posts page, and the listings it appears in with their pagination: its post type archive, every category, tag or term it is in, its author’s archive, and its month and year archives
A category, tag or term editedIts listing with its pagination, and the front page
A term deleted; a theme switched; a plugin activated or deactivated; a plugin, theme or WordPress core updated; a menu or widget changed; the Customizer saved; theme settings or widgets stored as options; permalinks, the site title, tagline, front page, posts page, reading settings, site icon, date format or language changedEverything — then the preload starts

A purge of everything is one rename of the pages folder, so it takes the same instant whether the site has 50 pages or 20,000; the old files are deleted by the hourly garbage collection. A page that was still rendering when the purge happened is never stored afterwards — the request’s start time is compared with the purge stamp — so a slow request cannot put a stale page back.

From WPClientHub:

  • Purge all and Purge URLs… on the card (owners, admins and techs). Everything, or up to 50 addresses at a time — addresses that are not the site’s are ignored and listed back. Done in the request, through the site, with the result in the Activity Log.
  • After an update batch (manual or AutoPilot), a restore or rollback, a database cleanup and an image run, on its own. The site also purges itself the moment WPClientHub’s updates are installed and the moment a restore finishes, so AutoPilot’s after-screenshots never see a stale page; the purge WPClientHub sends afterwards then comes back “already purged a moment ago” and is logged as that. A full purge within a minute of the last one is reported rather than repeated, so nothing restarts the preload twice.
  • On a site linked to Cloudflare (Scale) with auto-purge on, the same events purge the zone too — one fan-out, so no page is fresh on the site and stale at the edge.
  • Through the public API on Scale: POST /api/v1/sites/{site}/cache/purge. See the API.

Every purge from WPClientHub is one line in the Activity Log (cache.purged, with the reason and what was purged — the WPClientHub cache, the other plugin, or “by the site itself”), and the site keeps its own log of the last twenty purges with their reasons, shown on the card.

Preload

After every full purge (and from the card on demand) the site warms its cache again: the front page, the posts page, then the newest 500 published posts and pages, newest first — the set its XML sitemap lists. It requests them from itself with a header the cache recognises, a few pages per WP-Cron tick inside a time budget, never inside a visitor’s request, and skips pages that are already fresh. Pages visitors open meanwhile are cached as they go. If the site cannot reach itself (a host that blocks loopback requests) the preload stops after a few failures and the card says so — the cache still fills as visitors browse. Switch it off in Settings for a site that should not spend its cron on it.

Settings

Per site, from the card’s Settings button. Saving sends the settings to the site as one signed instruction (the same way the white-label and image settings travel); the card says Applied once the site has confirmed them, or Pending until its next sync. Anything that changes what a stored page looks like — phone copies, cookies, query parameters, exclusions, minify, defer — empties the cache on the site first.

SettingDefaultWhat it does
Cache pages on this siteOffOn installs the drop-in, the WP_CACHE line and the .htaccess block. Off removes all three (only what WPClientHub added) and every cached page; the other settings are kept.
Expire cached pages after10 hours1 to 720. Ten keeps pages under WordPress’s 12-hour nonce lifetime, so comment and contact forms on cached pages keep working. A change is purged at once whatever this says.
Warm the cache after a full purgeOnThe preload described above.
Separate copies for phonesOffOnly for a theme that serves different HTML to phones. Doubles the cache and keeps serving in PHP.
How cached pages are answeredPHP drop-inOr the web server on Apache/LiteSpeed, self-checked (see above).
Browser cachingOnExpires and Cache-Control for static files: 30 days for CSS and JavaScript (WordPress versions their URLs with ?ver=, so a new version is a new address), a year for images, fonts and video, immutable for files with a content hash in the name.
CompressionOnBrotli or gzip for HTML, CSS, JavaScript, JSON, XML, SVG and fonts through mod_brotli / mod_deflate, whichever the server has. Cached pages are served pre-compressed to browsers that accept gzip.
Minify HTMLOffRemoves comments and collapses whitespace between tags in the stored copy — never inside <pre>, <textarea>, <script> or <style>, and IE conditional comments stay. Off because a theme that relies on whitespace between inline elements can shift by a pixel.
Defer non-critical scriptsOffAdds defer to external scripts that have neither async nor defer; jQuery, modules, non-JavaScript types and scripts marked data-wpch-nodefer are left alone. Off because an inline script that calls another script straight away breaks when that script is deferred — test the site after switching it on.
Never cache these paths—Up to 50, starting with /. /members covers that path and everything below it; /members/* only what is below.
Bypass cookies—Up to 20 name prefixes; a visitor carrying one always gets a fresh page (a membership plugin’s cookie, say). The WordPress, WooCommerce and EDD cookies above always bypass.
Ignored query parametersutm_*, gclid, fbclid …Up to 30; a trailing * is a prefix. A request with only these gets the cached page; any other query string bypasses the cache.

Other cache plugins and hosts

Two page caches on top of each other only make purging harder, so when one of these is active WPClientHub’s own cache stays off (its drop-in, WP_CACHE line and .htaccess block are not installed, or are removed) and the card says Handled by …. Purging from the dashboard, the automatic purges and the API still work through that plugin’s or host’s own purge function, where it has one:

FoundPurge from WPClientHub
WP RocketEverything, or the given URLs
LiteSpeed CacheEverything, or the given URLs
W3 Total CacheEverything, or the given URLs
WP Super CacheEverything, or the given URLs
WP Fastest CacheEverything
Cache EnablerEverything, or the given URLs
Breeze (Cloudways)Everything, including its Varnish
Speed Optimizer (SiteGround)Everything, or the given URLs
KinstaEverything, through the Kinsta MU plugin
WP EngineEverything (object and page cache)
PantheonEverything, or the given paths
Cloudways (without Breeze)From the Cloudways console — the card says so
FlywheelFrom the Flywheel dashboard — Flywheel purges on content changes itself

Plugins are recognised by their plugin file, hosts by the constants and environment they set. A purge function that is missing or that throws is reported on the card and in the Activity Log, never a fatal error on the site. Deactivate the other plugin and WPClientHub’s cache takes over on the site’s next check-in, if it is switched on.

In client reports

A site whose cache has counted page views gets one line in the Performance section of its client report: “Cache: 90.4% of page views were served from the cache this week (13,730 page views, 184 pages cached).” Sites without data — the cache off, or handled by another plugin — get no line.

On the public API

On Scale, GET /api/v1/sites/{site}/cache (scope sites:read) returns the state, who handles it, the expiry, how pages are served, cached pages and bytes, hits and misses over seven days with the ratio, and the last purge. POST /api/v1/sites/{site}/cache/purge (scope sites:write) purges everything, or the urls you send — done in the request, through the site, logged with the key’s name. See the API reference.

Limits and honest notes

  • Multisite networks are not supported yet; the card says so and nothing is installed.
  • Plain permalinks (?p=123) cannot be cached — every page would be a query string. The card warns; switch the site to pretty permalinks.
  • The hit ratio counts page views the cache saw: hits answered by the drop-in or (on nginx, with the snippet) not counted at all, and misses WordPress rendered. Bots count like anyone else. It is a measure of the cache, not analytics.
  • A page a plugin builds per visitor without a cookie or DONOTCACHEPAGE (a geolocation banner, a random testimonial) is cached like any other page. Exclude its path.
  • The drop-in reads a small config file on every uncached request; on a site with caching off it is not there and nothing runs. The hourly garbage collection and the preload use the site’s WP-Cron — a site whose cron never runs (DISABLE_WP_CRON without a system cron) keeps expired files a little longer and does not preload.
  • No object cache, no fragment cache, no CDN. The Cloudflare integration on Scale covers the edge; this covers the origin.

Troubleshooting

The card says “Blocked on the site”
Something on the site is in the way and the card names it: another plugin’s advanced-cache.php left in wp-content (remove it, or that plugin), WP_CACHE set to false in wp-config.php (remove that line), or wp-content, wp-content/cache or wp-config.php not writable (fix the permissions, or add the WP_CACHE line yourself). The site checks again on every sync and switches the cache on by itself once the way is clear; nothing was changed meanwhile.
Served from cache stays low
Right after a purge every page view is a miss until the preload and visitors have filled the cache again. A persistently low ratio usually means most visitors carry a bypass cookie (a membership or consent plugin that sets one for everyone — add nothing to the bypass list you do not need), or most traffic goes to pages with query strings. Check the cookie names in the site’s own tools and the excluded paths.
A visitor sees stale content
Check the site’s purge log on the card: content changes purge their page at once. A page that changed through something WordPress fires no hook for — a plugin writing its own table, a shortcode reading an external feed — is only refreshed when it expires. Lower the expiry, or exclude that path. If a cache plugin or the host caches too, purge it as well (the card does both).
Web-server serving says the rules are not in effect
The self-check got the page from PHP rather than from the cache folder: mod_rewrite is off, AllowOverride is None for the site’s folder, or a proxy in front answered. The PHP drop-in serves cached pages meanwhile, still before WordPress loads. Fix the cause and press Check now; the rules are tried again.
The preload stopped with an error
The site could not request its own pages — some hosts block loopback requests, or the site answers only on a different hostname. The cache fills as visitors browse; press Try again after the host fixes it, or switch the preload off for that site.
I deleted the plugin by hand and want to be sure nothing is left
The drop-in fails open and does nothing without the plugin. Deleting the plugin through WordPress (uninstall) removes advanced-cache.php, the WP_CACHE line WPClientHub added, its .htaccess block and wp-content/cache/wpclienthub/. After a manual delete, remove those four yourself; the drop-in file says in its first lines whose it is.