Skip to content

Commit 648a7f9

Browse files
Improve Site Health page cache detection. See #64370
1 parent df52bf2 commit 648a7f9

File tree

1 file changed

+180
-113
lines changed

1 file changed

+180
-113
lines changed

src/wp-admin/includes/class-wp-site-health.php

Lines changed: 180 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,70 @@ private function perform_test( $callback ) {
194194
return apply_filters( 'site_status_test_result', call_user_func( $callback ) );
195195
}
196196

197+
/**
198+
* Detect cache status from headers.
199+
*
200+
* @param array $headers Response headers.
201+
* @return bool Whether cache was detected.
202+
*/
203+
private function detect_cache_headers( $headers ) {
204+
205+
// Normalize header names & values.
206+
$normalized = array();
207+
foreach ( $headers as $key => $value ) {
208+
$normalized[ strtolower( $key ) ] = strtolower( $value );
209+
}
210+
211+
// Common server / CDN / proxy cache headers.
212+
$cache_headers = array(
213+
'x-cache',
214+
'x-cache-status',
215+
'cf-cache-status',
216+
'x-varnish',
217+
'x-pass',
218+
'x-proxy-cache',
219+
'x-litespeed-cache',
220+
);
221+
222+
/**
223+
* Allow hosts / plugins to add additional cache headers.
224+
*/
225+
$cache_headers = apply_filters( 'site_health_cache_headers', $cache_headers );
226+
227+
// Valid cache status values.
228+
$valid_values = array(
229+
'hit',
230+
'miss',
231+
'pass',
232+
'skip',
233+
'expired',
234+
'revalidated',
235+
'reload',
236+
'refresh',
237+
'dynamic',
238+
'bypass',
239+
);
240+
241+
foreach ( $cache_headers as $header ) {
242+
if ( isset( $normalized[ $header ] ) ) {
243+
244+
$value = trim( $normalized[ $header ] );
245+
246+
// Exact match.
247+
if ( in_array( $value, $valid_values, true ) ) {
248+
return true;
249+
}
250+
251+
// Match phrases: "HIT from varnish", etc.
252+
if ( preg_match( '/\b(' . implode( '|', $valid_values ) . ')\b/i', $value ) ) {
253+
return true;
254+
}
255+
}
256+
}
257+
258+
return false;
259+
}
260+
197261
/**
198262
* Runs the SQL version checks.
199263
*
@@ -2407,110 +2471,123 @@ public function get_test_authorization_header() {
24072471
* @return array The test result.
24082472
*/
24092473
public function get_test_page_cache() {
2410-
$description = '<p>' . __( 'Page cache enhances the speed and performance of your site by saving and serving static pages instead of calling for a page every time a user visits.' ) . '</p>';
2411-
$description .= '<p>' . __( 'Page cache is detected by looking for an active page cache plugin as well as making three requests to the homepage and looking for one or more of the following HTTP client caching response headers:' ) . '</p>';
2412-
$description .= '<code>' . implode( '</code>, <code>', array_keys( $this->get_page_cache_headers() ) ) . '.</code>';
2413-
2414-
$result = array(
2415-
'badge' => array(
2416-
'label' => __( 'Performance' ),
2417-
'color' => 'blue',
2418-
),
2419-
'description' => wp_kses_post( $description ),
2420-
'test' => 'page_cache',
2421-
'status' => 'good',
2422-
'label' => '',
2423-
'actions' => sprintf(
2424-
'<p><a href="%1$s" target="_blank" rel="noreferrer">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
2425-
__( 'https://developer.wordpress.org/advanced-administration/performance/optimization/#caching' ),
2426-
__( 'Learn more about page cache' ),
2427-
/* translators: Hidden accessibility text. */
2428-
__( '(opens in a new tab)' )
2429-
),
2430-
);
2431-
2432-
$page_cache_detail = $this->get_page_cache_detail();
2433-
2434-
if ( is_wp_error( $page_cache_detail ) ) {
2435-
$result['label'] = __( 'Unable to detect the presence of page cache' );
2436-
$result['status'] = 'recommended';
2437-
$error_info = sprintf(
2438-
/* translators: 1: Error message, 2: Error code. */
2439-
__( 'Unable to detect page cache due to possible loopback request problem. Please verify that the loopback request test is passing. Error: %1$s (Code: %2$s)' ),
2440-
$page_cache_detail->get_error_message(),
2441-
$page_cache_detail->get_error_code()
2442-
);
2443-
$result['description'] = wp_kses_post( "<p>$error_info</p>" ) . $result['description'];
2444-
return $result;
2445-
}
24462474

2447-
$result['status'] = $page_cache_detail['status'];
2448-
2449-
switch ( $page_cache_detail['status'] ) {
2450-
case 'recommended':
2451-
$result['label'] = __( 'Page cache is not detected but the server response time is OK' );
2452-
break;
2453-
case 'good':
2454-
$result['label'] = __( 'Page cache is detected and the server response time is good' );
2455-
break;
2456-
default:
2457-
if ( empty( $page_cache_detail['headers'] ) && ! $page_cache_detail['advanced_cache_present'] ) {
2458-
$result['label'] = __( 'Page cache is not detected and the server response time is slow' );
2459-
} else {
2460-
$result['label'] = __( 'Page cache is detected but the server response time is still slow' );
2461-
}
2462-
}
2463-
2464-
$page_cache_test_summary = array();
2465-
2466-
if ( empty( $page_cache_detail['response_time'] ) ) {
2467-
$page_cache_test_summary[] = '<span class="dashicons dashicons-dismiss" aria-hidden="true"></span> ' . __( 'Server response time could not be determined. Verify that loopback requests are working.' );
2468-
} else {
2469-
2470-
$threshold = $this->get_good_response_time_threshold();
2471-
if ( $page_cache_detail['response_time'] < $threshold ) {
2472-
$page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span> ' . sprintf(
2473-
/* translators: 1: The response time in milliseconds, 2: The recommended threshold in milliseconds. */
2474-
__( 'Median server response time was %1$s milliseconds. This is less than the recommended %2$s milliseconds threshold.' ),
2475-
number_format_i18n( $page_cache_detail['response_time'] ),
2476-
number_format_i18n( $threshold )
2477-
);
2478-
} else {
2479-
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning" aria-hidden="true"></span> ' . sprintf(
2480-
/* translators: 1: The response time in milliseconds, 2: The recommended threshold in milliseconds. */
2481-
__( 'Median server response time was %1$s milliseconds. It should be less than the recommended %2$s milliseconds threshold.' ),
2482-
number_format_i18n( $page_cache_detail['response_time'] ),
2483-
number_format_i18n( $threshold )
2484-
);
2485-
}
2486-
2487-
if ( empty( $page_cache_detail['headers'] ) ) {
2488-
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning" aria-hidden="true"></span> ' . __( 'No client caching response headers were detected.' );
2489-
} else {
2490-
$headers_summary = '<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>';
2491-
$headers_summary .= ' ' . sprintf(
2492-
/* translators: %d: Number of caching headers. */
2493-
_n(
2494-
'There was %d client caching response header detected:',
2495-
'There were %d client caching response headers detected:',
2496-
count( $page_cache_detail['headers'] )
2497-
),
2498-
count( $page_cache_detail['headers'] )
2499-
);
2500-
$headers_summary .= ' <code>' . implode( '</code>, <code>', $page_cache_detail['headers'] ) . '</code>.';
2501-
$page_cache_test_summary[] = $headers_summary;
2502-
}
2503-
}
2504-
2505-
if ( $page_cache_detail['advanced_cache_present'] ) {
2506-
$page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span> ' . __( 'A page cache plugin was detected.' );
2507-
} elseif ( ! ( is_array( $page_cache_detail ) && ! empty( $page_cache_detail['headers'] ) ) ) {
2508-
// Note: This message is not shown if client caching response headers were present since an external caching layer may be employed.
2509-
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning" aria-hidden="true"></span> ' . __( 'A page cache plugin was not detected.' );
2510-
}
2511-
2512-
$result['description'] .= '<ul><li>' . implode( '</li><li>', $page_cache_test_summary ) . '</li></ul>';
2513-
return $result;
2475+
$description = '<p>' . __( 'Page cache enhances the speed and performance of your site by saving and serving static pages instead of calling for a page every time a user visits.' ) . '</p>';
2476+
$description .= '<p>' . __( 'Page cache is detected by looking for an active page cache plugin as well as making three requests to the homepage and looking for one or more of the following HTTP client caching response headers:' ) . '</p>';
2477+
$description .= '<code>' . implode( '</code>, <code>', array_keys( $this->get_page_cache_headers() ) ) . '.</code>';
2478+
2479+
$result = array(
2480+
'badge' => array(
2481+
'label' => __( 'Performance' ),
2482+
'color' => 'blue',
2483+
),
2484+
'description' => wp_kses_post( $description ),
2485+
'test' => 'page_cache',
2486+
'status' => 'good',
2487+
'label' => '',
2488+
'actions' => sprintf(
2489+
'<p><a href="%1$s" target="_blank" rel="noreferrer">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
2490+
__( 'https://developer.wordpress.org/advanced-administration/performance/optimization/#caching' ),
2491+
__( 'Learn more about page cache' ),
2492+
__( '(opens in a new tab)' )
2493+
),
2494+
);
2495+
2496+
// Get detailed page cache info.
2497+
$page_cache_detail = $this->get_page_cache_detail();
2498+
2499+
if ( is_wp_error( $page_cache_detail ) ) {
2500+
$result['label'] = __( 'Unable to detect the presence of page cache' );
2501+
$result['status'] = 'recommended';
2502+
$error_info = sprintf(
2503+
__( 'Unable to detect page cache due to possible loopback request problem. Please verify that the loopback request test is passing. Error: %1$s (Code: %2$s)' ),
2504+
$page_cache_detail->get_error_message(),
2505+
$page_cache_detail->get_error_code()
2506+
);
2507+
$result['description'] = wp_kses_post( "<p>$error_info</p>" ) . $result['description'];
2508+
return $result;
2509+
}
2510+
2511+
// Override default WP header detection with YOUR improved detection
2512+
$cache_detected = false;
2513+
if ( ! empty( $page_cache_detail['response_headers_raw'] ) ) {
2514+
foreach ( $page_cache_detail['response_headers_raw'] as $header_group ) {
2515+
if ( $this->detect_cache_headers( $header_group ) ) {
2516+
$cache_detected = true;
2517+
break;
2518+
}
2519+
}
2520+
}
2521+
2522+
// Rewrite status logic using new detection
2523+
if ( $cache_detected ) {
2524+
$page_cache_detail['headers'] = array( 'cache-detected' ); // fake entry to trigger WP's UI output
2525+
$page_cache_detail['status'] = 'good';
2526+
} else {
2527+
$page_cache_detail['headers'] = array();
2528+
$page_cache_detail['status'] = 'recommended';
2529+
}
2530+
2531+
$result['status'] = $page_cache_detail['status'];
2532+
2533+
switch ( $page_cache_detail['status'] ) {
2534+
case 'recommended':
2535+
$result['label'] = __( 'Page cache is not detected but the server response time is OK' );
2536+
break;
2537+
2538+
case 'good':
2539+
$result['label'] = __( 'Page cache is detected and the server response time is good' );
2540+
break;
2541+
2542+
default:
2543+
if ( empty( $page_cache_detail['headers'] ) && ! $page_cache_detail['advanced_cache_present'] ) {
2544+
$result['label'] = __( 'Page cache is not detected and the server response time is slow' );
2545+
} else {
2546+
$result['label'] = __( 'Page cache is detected but the server response time is still slow' );
2547+
}
2548+
}
2549+
2550+
$page_cache_test_summary = array();
2551+
2552+
// Response time
2553+
if ( empty( $page_cache_detail['response_time'] ) ) {
2554+
$page_cache_test_summary[] = '<span class="dashicons dashicons-dismiss"></span> ' . __( 'Server response time could not be determined. Verify that loopback requests are working.' );
2555+
} else {
2556+
$threshold = $this->get_good_response_time_threshold();
2557+
2558+
if ( $page_cache_detail['response_time'] < $threshold ) {
2559+
$page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt"></span> ' . sprintf(
2560+
__( 'Median server response time was %1$s milliseconds. This is less than the recommended %2$s milliseconds threshold.' ),
2561+
number_format_i18n( $page_cache_detail['response_time'] ),
2562+
number_format_i18n( $threshold )
2563+
);
2564+
} else {
2565+
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . sprintf(
2566+
__( 'Median server response time was %1$s milliseconds. It should be less than the recommended %2$s milliseconds threshold.' ),
2567+
number_format_i18n( $page_cache_detail['response_time'] ),
2568+
number_format_i18n( $threshold )
2569+
);
2570+
}
2571+
2572+
// Your new header detection integrated here
2573+
if ( ! $cache_detected ) {
2574+
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . __( 'No client caching response headers were detected.' );
2575+
} else {
2576+
$page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt"></span> ' .
2577+
__( 'Page cache headers were detected using enhanced header analysis.' );
2578+
}
2579+
}
2580+
2581+
// Page cache plugin availability
2582+
if ( $page_cache_detail['advanced_cache_present'] ) {
2583+
$page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt"></span> ' . __( 'A page cache plugin was detected.' );
2584+
} elseif ( ! $cache_detected ) {
2585+
$page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . __( 'A page cache plugin was not detected.' );
2586+
}
2587+
2588+
$result['description'] .= '<ul><li>' . implode( '</li><li>', $page_cache_test_summary ) . '</li></ul>';
2589+
2590+
return $result;
25142591
}
25152592

25162593
/**
@@ -3412,16 +3489,6 @@ public function get_page_cache_headers() {
34123489
},
34133490
'x-srcache-store-status' => $cache_hit_callback,
34143491
'x-srcache-fetch-status' => $cache_hit_callback,
3415-
3416-
// Generic caching proxies (Nginx, Varnish, etc.)
3417-
'x-cache' => $cache_hit_callback,
3418-
'x-cache-status' => $cache_hit_callback,
3419-
'x-litespeed-cache' => $cache_hit_callback,
3420-
'x-proxy-cache' => $cache_hit_callback,
3421-
'via' => '',
3422-
3423-
// Cloudflare
3424-
'cf-cache-status' => $cache_hit_callback,
34253492
);
34263493

34273494
/**

0 commit comments

Comments
 (0)