Skip to content
ThemesIonic — home
Troubleshooting

Page Cache Is Not Detected: What Site Health Actually Tested

Site Health requests your homepage three times and looks for caching headers or a cache drop-in. The warning reports what that test saw, which is not always the same as whether your pages are cached.

5 min read intermediate

Site Health requested your homepage three times, found no caching header it recognises and no active cache drop-in, and reported that as "Page cache is not detected". It is a test of what your server's responses look like, not a direct look at your cache. A site with no cache fails it, but so can a site whose cache works and labels its responses in a way WordPress does not check for.

The four results that are not a pass

The test lives in WP_Site_Health::get_test_page_cache(). Apart from the passing "Page cache is detected and the server response time is good", it produces one of these labels:

Label Status What it means
Page cache is not detected but the server response time is OK Recommended No cache signal, median response under the threshold
Page cache is not detected and the server response time is slow Critical No cache signal, median response at or over the threshold
Page cache is detected but the server response time is still slow Critical A cache signal was found, the homepage is still slow
Unable to detect the presence of page cache Recommended The requests themselves failed

The threshold is 600 milliseconds by default, adjustable with the site_status_good_response_time_threshold filter. The figure compared with it is the median of the three requests, so one slow cold request does not fail the test on its own.

How the detection works

WordPress calls wp_remote_get( home_url( '/' ) ) three times from your own server — a loopback request — and inspects the response headers each time. Cache is "detected" if at least one recognised header passes its check, or if a cache drop-in is active. The headers it checks:

  • Standard headers: cache-control with a non-zero max-age, expires set in the future, age above zero, and last-modified, etag or via simply being present.
  • Cache-layer headers that must say HIT: cf-cache-status, x-cache, x-litespeed-cache, x-srcache-fetch-status, x-cache-status, x-proxy-cache.
  • Others: x-srcache-store-status: STORE, an x-varnish header with two IDs, and two custom flags, x-cache-enabled and x-cache-disabled.

The drop-in check needs three things at once: wp-content/advanced-cache.php exists, WP_CACHE is defined as true, and the enable_loading_advanced_cache_dropin filter has not been switched off.

Run the same request yourself and look at what comes back. Repeat it, because the first response after a purge is usually a miss:

curl -sI https://example.com/ | grep -iE 'cache|age|expires|etag|last-modified|via|varnish'
curl -sI https://example.com/ | grep -iE 'cache|age|expires|etag|last-modified|via|varnish'

Why a working cache still fails the test

The plugin is installed but WP_CACHE is not set. Page cache plugins write advanced-cache.php and add define( 'WP_CACHE', true ); to wp-config.php. If wp-config.php was not writable, or a later deployment overwrote it, the drop-in file sits there unused and nothing is cached. What the WP_CACHE constant actually does covers the full chain.

A CDN serves the page, but not as a HIT. Cloudflare does not cache HTML by default: pages pass through with cf-cache-status: DYNAMIC, and the test accepts that header only when it contains HIT. The CDN is in front of the site, yet the page itself is fetched from your server every time. Cloudflare and WordPress explains what it caches out of the box.

The loopback request bypasses the CDN. The test runs from your server to your own domain. If the server resolves its domain to itself — through a hosts-file entry or local DNS — the request never reaches the CDN, so its headers never appear. Your visitors are cached; the test is not.

The cache keeps its own header. Some server-level caches add a custom header such as x-host-cache: HIT. WordPress does not know it, so you can add it:

add_filter( 'site_status_page_cache_supported_cache_headers', function ( $headers ) {
    $headers['x-host-cache'] = static function ( $value ) {
        return false !== stripos( $value, 'hit' );
    };
    return $headers;
} );

The requests fail. "Unable to detect the presence of page cache" means a loopback error or a non-200 response, and the message includes the error code. That is a loopback request problem, not a cache problem. WordPress follows up to five redirects, so a redirecting homepage is fine as long as it ends in a 200; a final 401, 403 or 404 — a login wall, for example — aborts the test.

The trap: a pass is not proof either

The detection is generous. etag, last-modified and via count as cache signals just by being present, whatever their value. A site with no page cache at all passes if a proxy adds Via or a plugin sends Last-Modified. Read the details under the result, where WordPress lists the headers it found, before treating a green result as a working cache.

Response time is the part that matters to visitors. If the result says the cache is detected but the response time is still slow, the cache is probably bypassed for the homepage — a cookie, a query string or an exclusion rule. Excluding a page from cache shows where those rules live in each layer.

When there really is no cache

If neither the headers nor the drop-in show anything, the site renders every page in PHP on every visit. Add page caching at one layer — the host's server cache, a cache plugin, or a CDN rule that caches HTML — not all three at once. How to speed up a WordPress site puts page caching first for that reason, and reducing server response time covers what to do when even uncached responses are slow.

Common mistakes

  • Installing a second cache plugin because the first "was not detected", when the real cause was a missing WP_CACHE constant.
  • Reading the test on a staging copy. Unless its environment type is production, the test does not run there at all.
  • Treating cf-cache-status: DYNAMIC as cached.
  • Hiding the test instead of teaching it your cache's header.

Verify

  • curl -sI twice in a row shows a HIT, an age above zero, or another header from the list above.
  • Tools › Site Health › Status, with the page reloaded, shows "Page cache is detected and the server response time is good".
  • The details under the result list the headers you expect, not just an incidental etag.
  • After a full cache clear, the second request is a HIT again.

Frequently asked

WordPress registers the page cache test only when the environment type is production. If WP_ENVIRONMENT_TYPE is set to staging, development or local, the test is skipped, so a staging copy never shows the warning even when the live site does.
It is a recommendation, not a critical issue. The median of three homepage requests was under 600 milliseconds, so visitors get a reasonably fast first byte. A cache would still help under load, but nothing is broken.
You can remove the test with the site_status_tests filter, but that only hides the result. If the warning is a false negative because your cache sends a header WordPress does not know, add that header with the site_status_page_cache_supported_cache_headers filter instead, so the test stays useful.

Related guides