From d4817d6e90b64b469f3f66c251e2e7420dc08023 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 3 Feb 2026 09:27:32 -0800 Subject: [PATCH 01/14] Site Health: Improve page cache detection robustness. This commit updates the page cache detection in Site Health by: - Using a stricter regex (\bhit\b) for detecting cache hits in response headers, preventing false positives (e.g., 'shit'). - Adding support for the X-Varnish header, identifying a cache hit when it contains two request IDs. - Adding documentation and source links for the supported headers. - Adding PHPUnit test cases to verify the new detection logic. Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../includes/class-wp-site-health.php | 34 ++++++++++++++----- tests/phpunit/tests/admin/wpSiteHealth.php | 27 +++++++++++++++ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index dd537296a8655..d2649e031e1d3 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3390,10 +3390,11 @@ public function is_development_environment() { public function get_page_cache_headers() { $cache_hit_callback = static function ( $header_value ) { - return str_contains( strtolower( $header_value ), 'hit' ); + return (bool) preg_match( '/\bhit\b/i', $header_value ); }; $cache_headers = array( + // Standard HTTP caching headers. 'cache-control' => static function ( $header_value ) { return (bool) preg_match( '/max-age=[1-9]/', $header_value ); }, @@ -3405,24 +3406,39 @@ public function get_page_cache_headers() { }, 'last-modified' => '', 'etag' => '', + + // Custom caching headers. 'x-cache-enabled' => static function ( $header_value ) { return 'true' === strtolower( $header_value ); }, 'x-cache-disabled' => static function ( $header_value ) { return ( 'on' !== strtolower( $header_value ) ); }, + // OpenResty srcache-nginx-module. 'x-srcache-store-status' => $cache_hit_callback, 'x-srcache-fetch-status' => $cache_hit_callback, - // Generic caching proxies (Nginx, Varnish, etc.) - 'x-cache' => $cache_hit_callback, - 'x-cache-status' => $cache_hit_callback, - 'x-litespeed-cache' => $cache_hit_callback, - 'x-proxy-cache' => $cache_hit_callback, - 'via' => '', + // Generic caching proxies (Nginx, Varnish, etc.). + 'x-cache' => $cache_hit_callback, + 'x-cache-status' => $cache_hit_callback, + 'x-litespeed-cache' => $cache_hit_callback, + 'x-proxy-cache' => $cache_hit_callback, + + /** + * Varnish Cache. + * + * For a cache hit, it includes both the ID of the current request and the ID of the request + * that populated the cache. For a miss, it only includes the current request ID. + * + * @link https://vinyl-cache.org/docs/2.1/faq/http.html + */ + 'x-varnish' => static function ( $header_value ) { + return (bool) preg_match( '/\d+ \d+/', $header_value ); + }, + 'via' => '', - // Cloudflare - 'cf-cache-status' => $cache_hit_callback, + // Cloudflare. + 'cf-cache-status' => $cache_hit_callback, ); /** diff --git a/tests/phpunit/tests/admin/wpSiteHealth.php b/tests/phpunit/tests/admin/wpSiteHealth.php index 2d32bbb14ec4d..fa2ba9cd74480 100644 --- a/tests/phpunit/tests/admin/wpSiteHealth.php +++ b/tests/phpunit/tests/admin/wpSiteHealth.php @@ -408,6 +408,33 @@ public function data_get_page_cache() { 'expected_status' => 'good', 'expected_label' => $good_label, ), + 'false-positive-hit-in-word' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-cache' => 'shit' ) + ), + 'expected_status' => 'recommended', + 'expected_label' => $recommended_label, + ), + 'varnish-header' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-varnish' => '123 456' ) + ), + 'expected_status' => 'good', + 'expected_label' => $good_label, + ), + 'varnish-header-miss' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-varnish' => '123' ) + ), + 'expected_status' => 'recommended', + 'expected_label' => $recommended_label, + ), ); } From a2d3183abcb19f41a11c177624cdabd0a19b2c4b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 3 Feb 2026 09:31:28 -0800 Subject: [PATCH 02/14] Site Health: Fix detection for x-srcache-store-status header. The x-srcache-store-status header uses 'STORE' or 'BYPASS', not 'HIT' or 'MISS'. This update ensures that 'STORE' is correctly identified as a positive page cache indicator. Also includes: - Documentation for srcache headers. - Explicit list of generic caching proxies (Squid, Go, Fastly, LiteSpeed) in comments. - Test cases for x-srcache-store-status and x-srcache-fetch-status. Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../includes/class-wp-site-health.php | 23 +++++++++++++--- tests/phpunit/tests/admin/wpSiteHealth.php | 27 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index d2649e031e1d3..da2dc0994bb4d 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3414,14 +3414,31 @@ public function get_page_cache_headers() { 'x-cache-disabled' => static function ( $header_value ) { return ( 'on' !== strtolower( $header_value ) ); }, - // OpenResty srcache-nginx-module. - 'x-srcache-store-status' => $cache_hit_callback, + + /** + * OpenResty srcache-nginx-module. + * + * The `x-srcache-store-status` header indicates if the response was stored in the cache. + * Valid values include `STORE` and `BYPASS`. + * + * The `x-srcache-fetch-status` header indicates if the response was fetched from the cache. + * Valid values include `HIT`, `MISS`, and `BYPASS`. + * + * @link https://github.com/openresty/srcache-nginx-module + */ + 'x-srcache-store-status' => static function ( $header_value ) { + return 'store' === strtolower( $header_value ); + }, 'x-srcache-fetch-status' => $cache_hit_callback, - // Generic caching proxies (Nginx, Varnish, etc.). + // Generic caching proxies (Nginx, Varnish, Squid, Go, Fastly, LiteSpeed, etc.). + 'x-cache' => $cache_hit_callback, + 'x-cache-status' => $cache_hit_callback, + 'x-litespeed-cache' => $cache_hit_callback, + 'x-proxy-cache' => $cache_hit_callback, /** diff --git a/tests/phpunit/tests/admin/wpSiteHealth.php b/tests/phpunit/tests/admin/wpSiteHealth.php index fa2ba9cd74480..f3dfdb3ba2502 100644 --- a/tests/phpunit/tests/admin/wpSiteHealth.php +++ b/tests/phpunit/tests/admin/wpSiteHealth.php @@ -435,6 +435,33 @@ public function data_get_page_cache() { 'expected_status' => 'recommended', 'expected_label' => $recommended_label, ), + 'srcache-store-status' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-srcache-store-status' => 'STORE' ) + ), + 'expected_status' => 'good', + 'expected_label' => $good_label, + ), + 'srcache-store-status-bypass' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-srcache-store-status' => 'BYPASS' ) + ), + 'expected_status' => 'recommended', + 'expected_label' => $recommended_label, + ), + 'srcache-fetch-status' => array( + 'responses' => array_fill( + 0, + 3, + array( 'x-srcache-fetch-status' => 'HIT' ) + ), + 'expected_status' => 'good', + 'expected_label' => $good_label, + ), ); } From 9aa2b9417603dae61da271880274cf460accdf77 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 3 Feb 2026 09:50:50 -0800 Subject: [PATCH 03/14] Improve docs and typing for get_page_cache_headers method --- .../includes/class-wp-site-health.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index da2dc0994bb4d..b161afdc3109b 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3378,16 +3378,13 @@ public function is_development_environment() { } /** - * Returns a list of headers and its verification callback to verify if page cache is enabled or not. - * - * Note: key is header name and value could be callable function to verify header value. - * Empty value mean existence of header detect page cache is enabled. + * Returns a mapping to response headers to an optional callback to verify if page cache is enabled or not. * * @since 6.1.0 * - * @return array List of client caching headers and their (optional) verification callbacks. + * @return array Mapping of page caching headers and their (optional) verification callbacks. */ - public function get_page_cache_headers() { + public function get_page_cache_headers(): array { $cache_hit_callback = static function ( $header_value ) { return (bool) preg_match( '/\bhit\b/i', $header_value ); @@ -3404,8 +3401,8 @@ public function get_page_cache_headers() { 'age' => static function ( $header_value ) { return is_numeric( $header_value ) && $header_value > 0; }, - 'last-modified' => '', - 'etag' => '', + 'last-modified' => null, + 'etag' => null, // Custom caching headers. 'x-cache-enabled' => static function ( $header_value ) { @@ -3452,7 +3449,7 @@ public function get_page_cache_headers() { 'x-varnish' => static function ( $header_value ) { return (bool) preg_match( '/\d+ \d+/', $header_value ); }, - 'via' => '', + 'via' => null, // Cloudflare. 'cf-cache-status' => $cache_hit_callback, @@ -3463,9 +3460,9 @@ public function get_page_cache_headers() { * * @since 6.1.0 * - * @param array $cache_headers Array of supported cache headers. + * @param array $cache_headers Mapping of page caching headers and their (optional) verification callbacks. */ - return apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers ); + return (array) apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers ); } /** From ed212565a1f8eaaf92c65077fe4c54e396e8da45 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 3 Feb 2026 10:39:40 -0800 Subject: [PATCH 04/14] Tidy test_get_page_cache --- .../includes/class-wp-site-health.php | 4 -- tests/phpunit/tests/admin/wpSiteHealth.php | 48 ++++++++++++++----- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index b161afdc3109b..57fb92a67a5a5 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3429,13 +3429,9 @@ public function get_page_cache_headers(): array { 'x-srcache-fetch-status' => $cache_hit_callback, // Generic caching proxies (Nginx, Varnish, Squid, Go, Fastly, LiteSpeed, etc.). - 'x-cache' => $cache_hit_callback, - 'x-cache-status' => $cache_hit_callback, - 'x-litespeed-cache' => $cache_hit_callback, - 'x-proxy-cache' => $cache_hit_callback, /** diff --git a/tests/phpunit/tests/admin/wpSiteHealth.php b/tests/phpunit/tests/admin/wpSiteHealth.php index f3dfdb3ba2502..05761765b327c 100644 --- a/tests/phpunit/tests/admin/wpSiteHealth.php +++ b/tests/phpunit/tests/admin/wpSiteHealth.php @@ -12,10 +12,8 @@ class Tests_Admin_wpSiteHealth extends WP_UnitTestCase { * An instance of the class to test. * * @since 6.1.0 - * - * @var WP_Site_Health */ - private $instance; + private WP_Site_Health $instance; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { // Include the `WP_Site_Health` file. @@ -172,7 +170,7 @@ public function data_cron_health_checks() { * @covers ::get_page_cache_headers() * @covers ::check_for_page_caching() */ - public function test_get_page_cache( $responses, $expected_status, $expected_label, $good_basic_auth = null, $delay_the_response = false ) { + public function test_get_page_cache( array $responses, string $expected_status, string $expected_label, bool $good_basic_auth = false, bool $delay_the_response = false ) { $expected_props = array( 'badge' => array( 'label' => __( 'Performance' ), @@ -183,7 +181,7 @@ public function test_get_page_cache( $responses, $expected_status, $expected_lab 'label' => $expected_label, ); - if ( null !== $good_basic_auth ) { + if ( $good_basic_auth ) { $_SERVER['PHP_AUTH_USER'] = 'admin'; $_SERVER['PHP_AUTH_PW'] = 'password'; } @@ -219,7 +217,7 @@ function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good ); } - if ( null !== $good_basic_auth ) { + if ( $good_basic_auth ) { $this->assertArrayHasKey( 'Authorization', $parsed_args['headers'] @@ -263,9 +261,15 @@ function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good * * @ticket 56041 * - * @return array[] + * @return array>, + * expected_status: 'recommended'|'critical'|'good', + * expected_label: string, + * good_basic_auth?: bool, + * delay_the_response?: bool, + * }> */ - public function data_get_page_cache() { + public function data_get_page_cache(): array { $recommended_label = 'Page cache is not detected but the server response time is OK'; $good_label = 'Page cache is detected and the server response time is good'; $critical_label = 'Page cache is not detected and the server response time is slow'; @@ -278,13 +282,13 @@ public function data_get_page_cache() { ), 'expected_status' => 'recommended', 'expected_label' => $error_label, - 'good_basic_auth' => false, + 'good_basic_auth' => true, ), 'no-cache-control' => array( 'responses' => array_fill( 0, 3, array() ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => null, + 'good_basic_auth' => false, 'delay_the_response' => true, ), 'no-cache' => array( @@ -310,7 +314,7 @@ public function data_get_page_cache() { 'responses' => array_fill( 0, 3, array( 'cache-control' => 'no-cache' ) ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => null, + 'good_basic_auth' => false, 'delay_the_response' => true, ), 'age' => array( @@ -366,7 +370,7 @@ public function data_get_page_cache() { ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => null, + 'good_basic_auth' => false, 'delay_the_response' => true, ), 'cache-control-with-basic-auth' => array( @@ -396,7 +400,7 @@ public function data_get_page_cache() { ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => null, + 'good_basic_auth' => false, 'delay_the_response' => true, ), 'x-cache-disabled' => array( @@ -462,6 +466,24 @@ public function data_get_page_cache() { 'expected_status' => 'good', 'expected_label' => $good_label, ), + 'last-modified' => array( + 'responses' => array_fill( + 0, + 3, + array( 'last-modified' => 'Wed, 21 Oct 2015 07:28:00 GMT' ) + ), + 'expected_status' => 'good', + 'expected_label' => $good_label, + ), + 'via' => array( + 'responses' => array_fill( + 0, + 3, + array( 'via' => '1.1 varnish' ) + ), + 'expected_status' => 'good', + 'expected_label' => $good_label, + ), ); } From 5a7b2f5fb4ff5c879032ec86227f5650666593ad Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 10:51:46 -0800 Subject: [PATCH 05/14] Rename good_basic_auth to has_basic_auth since not necessarily good --- tests/phpunit/tests/admin/wpSiteHealth.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/phpunit/tests/admin/wpSiteHealth.php b/tests/phpunit/tests/admin/wpSiteHealth.php index 313d8d3432743..58c49da04c489 100644 --- a/tests/phpunit/tests/admin/wpSiteHealth.php +++ b/tests/phpunit/tests/admin/wpSiteHealth.php @@ -170,7 +170,7 @@ public function data_cron_health_checks() { * @covers ::get_page_cache_headers() * @covers ::check_for_page_caching() */ - public function test_get_page_cache( array $responses, string $expected_status, string $expected_label, bool $good_basic_auth = false, bool $delay_the_response = false ) { + public function test_get_page_cache( array $responses, string $expected_status, string $expected_label, bool $has_basic_auth = false, bool $delay_the_response = false ) { $expected_props = array( 'badge' => array( 'label' => __( 'Performance' ), @@ -181,7 +181,7 @@ public function test_get_page_cache( array $responses, string $expected_status, 'label' => $expected_label, ); - if ( $good_basic_auth ) { + if ( $has_basic_auth ) { $_SERVER['PHP_AUTH_USER'] = 'admin'; $_SERVER['PHP_AUTH_PW'] = 'password'; } @@ -198,7 +198,7 @@ static function () use ( $threshold ) { add_filter( 'pre_http_request', - function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good_basic_auth, $delay_the_response, $threshold ) { + function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $has_basic_auth, $delay_the_response, $threshold ) { $expected_response = array_shift( $responses ); @@ -217,7 +217,7 @@ function ( $response, $parsed_args ) use ( &$responses, &$is_unauthorized, $good ); } - if ( $good_basic_auth ) { + if ( $has_basic_auth ) { $this->assertArrayHasKey( 'Authorization', $parsed_args['headers'] @@ -282,13 +282,13 @@ public function data_get_page_cache(): array { ), 'expected_status' => 'recommended', 'expected_label' => $error_label, - 'good_basic_auth' => true, + 'has_basic_auth' => true, ), 'no-cache-control' => array( 'responses' => array_fill( 0, 3, array() ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => false, + 'has_basic_auth' => false, 'delay_the_response' => true, ), 'no-cache' => array( @@ -314,7 +314,7 @@ public function data_get_page_cache(): array { 'responses' => array_fill( 0, 3, array( 'cache-control' => 'no-cache' ) ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => false, + 'has_basic_auth' => false, 'delay_the_response' => true, ), 'age' => array( @@ -370,7 +370,7 @@ public function data_get_page_cache(): array { ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => false, + 'has_basic_auth' => false, 'delay_the_response' => true, ), 'cache-control-with-basic-auth' => array( @@ -381,7 +381,7 @@ public function data_get_page_cache(): array { ), 'expected_status' => 'good', 'expected_label' => $good_label, - 'good_basic_auth' => true, + 'has_basic_auth' => true, ), 'x-cache-enabled' => array( 'responses' => array_fill( @@ -400,7 +400,7 @@ public function data_get_page_cache(): array { ), 'expected_status' => 'critical', 'expected_label' => $critical_label, - 'good_basic_auth' => false, + 'has_basic_auth' => false, 'delay_the_response' => true, ), 'x-cache-disabled' => array( From de5b813a37a65f6d6c086570b03c25531d4609f1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 11:31:42 -0800 Subject: [PATCH 06/14] Fix typo in comment Co-authored-by: Dennis Snell --- src/wp-admin/includes/class-wp-site-health.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index d28b0ff74f713..759f83caf2262 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3428,7 +3428,7 @@ public function is_development_environment() { } /** - * Returns a mapping to response headers to an optional callback to verify if page cache is enabled or not. + * Returns a mapping from response headers to an optional callback to verify if page cache is enabled or not. * * @since 6.1.0 * From c51daae4b5d1b637a3ecb68f8b9ff60b07295fec Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 11:35:30 -0800 Subject: [PATCH 07/14] Replace bool type cast with explicit equality for preg_match() return value Co-authored-by: Dennis Snell --- src/wp-admin/includes/class-wp-site-health.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 759f83caf2262..6cff91f3a6f6e 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3437,7 +3437,7 @@ public function is_development_environment() { public function get_page_cache_headers(): array { $cache_hit_callback = static function ( $header_value ) { - return (bool) preg_match( '/\bhit\b/i', $header_value ); + return 1 === preg_match( '/\bhit\b/i', $header_value ); }; $cache_headers = array( From 913d8cbc94c86af1056b80508bc27c54ef0f02f5 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 11:41:04 -0800 Subject: [PATCH 08/14] Remove vulgarity from test Co-authored-by: Dennis Snell --- tests/phpunit/tests/admin/wpSiteHealth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/admin/wpSiteHealth.php b/tests/phpunit/tests/admin/wpSiteHealth.php index 58c49da04c489..6080b477f54c3 100644 --- a/tests/phpunit/tests/admin/wpSiteHealth.php +++ b/tests/phpunit/tests/admin/wpSiteHealth.php @@ -416,7 +416,7 @@ public function data_get_page_cache(): array { 'responses' => array_fill( 0, 3, - array( 'x-cache' => 'shit' ) + array( 'x-cache' => 'no-hit' ) ), 'expected_status' => 'recommended', 'expected_label' => $recommended_label, From c63928a3116a66fb03da637bcc203606f84923fa Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 11:50:21 -0800 Subject: [PATCH 09/14] Move via header up to standard section --- src/wp-admin/includes/class-wp-site-health.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 6cff91f3a6f6e..24958ca65cba1 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3453,6 +3453,7 @@ public function get_page_cache_headers(): array { }, 'last-modified' => null, 'etag' => null, + 'via' => null, // Custom caching headers. 'x-cache-enabled' => static function ( $header_value ) { @@ -3495,7 +3496,6 @@ public function get_page_cache_headers(): array { 'x-varnish' => static function ( $header_value ) { return (bool) preg_match( '/\d+ \d+/', $header_value ); }, - 'via' => null, // Cloudflare. 'cf-cache-status' => $cache_hit_callback, From 6f1ff6acde89eeab1448fe6ebe5bf92161d46a80 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 12:05:51 -0800 Subject: [PATCH 10/14] Re-organized cache headers --- .../includes/class-wp-site-health.php | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 24958ca65cba1..648ad81cfecb2 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3457,11 +3457,34 @@ public function get_page_cache_headers(): array { // Custom caching headers. 'x-cache-enabled' => static function ( $header_value ) { - return 'true' === strtolower( $header_value ); + return ( 'true' === strtolower( $header_value ) ); }, 'x-cache-disabled' => static function ( $header_value ) { return ( 'on' !== strtolower( $header_value ) ); }, + 'x-cache-status' => $cache_hit_callback, // See . + 'x-proxy-cache' => $cache_hit_callback, + + /** + * CloudFlare. + * + * @link https://developers.cloudflare.com/cache/concepts/cache-responses/ + */ + 'cf-cache-status' => $cache_hit_callback, + + /** + * Fastly. + * + * @link https://www.fastly.com/documentation/reference/http/http-headers/X-Cache/ + */ + 'x-cache' => $cache_hit_callback, + + /** + * LightSpeed. + * + * @link https://docs.litespeedtech.com/lscache/devguide/controls/#x-litespeed-cache + */ + 'x-litespeed-cache' => $cache_hit_callback, /** * OpenResty srcache-nginx-module. @@ -3479,12 +3502,6 @@ public function get_page_cache_headers(): array { }, 'x-srcache-fetch-status' => $cache_hit_callback, - // Generic caching proxies (Nginx, Varnish, Squid, Go, Fastly, LiteSpeed, etc.). - 'x-cache' => $cache_hit_callback, - 'x-cache-status' => $cache_hit_callback, - 'x-litespeed-cache' => $cache_hit_callback, - 'x-proxy-cache' => $cache_hit_callback, - /** * Varnish Cache. * @@ -3496,9 +3513,6 @@ public function get_page_cache_headers(): array { 'x-varnish' => static function ( $header_value ) { return (bool) preg_match( '/\d+ \d+/', $header_value ); }, - - // Cloudflare. - 'cf-cache-status' => $cache_hit_callback, ); /** From 4af0c36e86eac4d8f6b937eefb0648303a568089 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 12:31:42 -0800 Subject: [PATCH 11/14] Add nginx section and add links for Varnish --- src/wp-admin/includes/class-wp-site-health.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 648ad81cfecb2..995b635f9976e 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3462,7 +3462,6 @@ public function get_page_cache_headers(): array { 'x-cache-disabled' => static function ( $header_value ) { return ( 'on' !== strtolower( $header_value ) ); }, - 'x-cache-status' => $cache_hit_callback, // See . 'x-proxy-cache' => $cache_hit_callback, /** @@ -3502,16 +3501,25 @@ public function get_page_cache_headers(): array { }, 'x-srcache-fetch-status' => $cache_hit_callback, + /** + * Nginx. + * + * @link https://blog.nginx.org/blog/nginx-caching-guide + */ + 'x-cache-status' => $cache_hit_callback, + /** * Varnish Cache. * - * For a cache hit, it includes both the ID of the current request and the ID of the request - * that populated the cache. For a miss, it only includes the current request ID. + * A header with a single number indicates it was not cached. If there are two numbers (or more), then this + * indicates the response was cached. * * @link https://vinyl-cache.org/docs/2.1/faq/http.html + * @link https://www.fastly.com/documentation/reference/http/http-headers/X-Varnish/ + * @link https://www.linuxjournal.com/content/speed-your-web-site-varnish */ 'x-varnish' => static function ( $header_value ) { - return (bool) preg_match( '/\d+ \d+/', $header_value ); + return 1 === preg_match( '/^\d+ \d+/', $header_value ); }, ); From 1aded1669df32781c8176b6d963671800cc5a6ab Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 12:34:28 -0800 Subject: [PATCH 12/14] Move x-proxy-cache into Nginx section --- src/wp-admin/includes/class-wp-site-health.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 995b635f9976e..fdb5d590201fe 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3462,7 +3462,6 @@ public function get_page_cache_headers(): array { 'x-cache-disabled' => static function ( $header_value ) { return ( 'on' !== strtolower( $header_value ) ); }, - 'x-proxy-cache' => $cache_hit_callback, /** * CloudFlare. @@ -3505,8 +3504,10 @@ public function get_page_cache_headers(): array { * Nginx. * * @link https://blog.nginx.org/blog/nginx-caching-guide + * @link https://www.inmotionhosting.com/support/website/nginx-cache-management/ */ 'x-cache-status' => $cache_hit_callback, + 'x-proxy-cache' => $cache_hit_callback, /** * Varnish Cache. From c67ffac028e840b3525c93ec6ced49409c0bd625 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 12:42:07 -0800 Subject: [PATCH 13/14] Replace word boundary for HIT with anchor, comma, or whitespace --- src/wp-admin/includes/class-wp-site-health.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index fdb5d590201fe..928136db8e24d 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3437,7 +3437,7 @@ public function is_development_environment() { public function get_page_cache_headers(): array { $cache_hit_callback = static function ( $header_value ) { - return 1 === preg_match( '/\bhit\b/i', $header_value ); + return 1 === preg_match( '/(^| |,)HIT(,| |$)/i', $header_value ); }; $cache_headers = array( From 40770d1008099678431569a0f341689248265282 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Feb 2026 12:48:20 -0800 Subject: [PATCH 14/14] Add documentation for x-cache-enabled and x-cache-disabled --- src/wp-admin/includes/class-wp-site-health.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 928136db8e24d..9afcffbd184cb 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -3455,7 +3455,17 @@ public function get_page_cache_headers(): array { 'etag' => null, 'via' => null, - // Custom caching headers. + /** + * Custom caching headers. + * + * These do not seem to be actually used by any caching layers. There were first introduced in a Site Health + * test in the AMP plugin. They were copied into the Performance Lab plugin's Site Health test before they + * were merged into core. + * + * @link https://github.com/ampproject/amp-wp/pull/6849 + * @link https://github.com/WordPress/performance/pull/263 + * @link https://core.trac.wordpress.org/changeset/54043 + */ 'x-cache-enabled' => static function ( $header_value ) { return ( 'true' === strtolower( $header_value ) ); },