$xspeed_cookie_value ) { unset( $xspeed_cookie_value ); $xspeed_cookie_name = (string) $xspeed_cookie_name; if ( 0 === strpos( $xspeed_cookie_name, 'wordpress_logged_in' ) || 0 === strpos( $xspeed_cookie_name, 'comment_author_' ) || 0 === strpos( $xspeed_cookie_name, 'wp-postpass_' ) ) { return; } } } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Drop-in runs before wp_unslash()/sanitize_text_field() are loaded. Value is filtered through a strict allowlist regex below (letters, digits, dot, hyphen, colon) and only used as md5() input for the cache key. $xspeed_host = isset( $_SERVER['HTTP_HOST'] ) ? (string) $_SERVER['HTTP_HOST'] : 'default'; $xspeed_host = str_replace( "\0", '', $xspeed_host ); // Restrict host to a safe charset (letters, digits, dot, hyphen, colon for port). $xspeed_host = preg_replace( '/[^a-zA-Z0-9.\-:]/', '', $xspeed_host ); $xspeed_path_only = strtok( $xspeed_request_uri, '?' ); // Device bucket — MUST mirror XSpeed\Cache::cache_key() exactly, or the key // the drop-in computes won't match the file Cache::store() wrote, the HIT // branch below never fires, and every request falls through to a full // WordPress boot (defeating the whole point of the pre-WP drop-in). // // Cache::cache_key() appends '|m' / '|d' when the cache module's // `mobile_separate` setting is on. The drop-in can't read WP options // (it runs before WordPress loads), so Cache writes a zero-byte sidecar // flag — `.mobile-separate` next to the cache files — whenever that setting // is on, and removes it when off (see Cache::sync_mobile_flag()). We mirror // the same UA token list wp_is_mobile() uses, the same one Cache's inline // fallback detector uses. $xspeed_device = ''; if ( file_exists( WP_CONTENT_DIR . '/cache/xspeed/.mobile-separate' ) ) { // Mirror core's wp_is_mobile() EXACTLY (which Cache::is_mobile_request() // defers to): check the Sec-CH-UA-Mobile client hint first, then fall // back to the same UA token list. Any divergence from the engine's // detection re-introduces the key mismatch this whole flag exists to // prevent. $xspeed_is_mobile = false; if ( isset( $_SERVER['HTTP_SEC_CH_UA_MOBILE'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Drop-in runs pre-WP. Value is compared against the literal '?1', never echoed or executed. $xspeed_is_mobile = ( '?1' === $_SERVER['HTTP_SEC_CH_UA_MOBILE'] ); } else { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Drop-in runs before wp_unslash()/sanitize_text_field() load. Value is only matched against a literal token regex, never echoed or executed. $xspeed_ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? (string) $_SERVER['HTTP_USER_AGENT'] : ''; $xspeed_is_mobile = (bool) preg_match( '/(Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi)/i', $xspeed_ua ); } $xspeed_device = $xspeed_is_mobile ? '|m' : '|d'; } $xspeed_cache_key = md5( $xspeed_host . $xspeed_path_only . $xspeed_device ); $xspeed_cache_file = WP_CONTENT_DIR . '/cache/xspeed/' . $xspeed_cache_key . '.html'; $xspeed_meta_file = WP_CONTENT_DIR . '/cache/xspeed/' . $xspeed_cache_key . '.meta'; if ( file_exists( $xspeed_cache_file ) ) { // Read the .meta sidecar (status / content_type / ttl) the same way the // PHP HIT path does — the drop-in serves cached feeds and 404s too, so it // must replay their Content-Type / status and honor their per-content TTL. // Ordinary 200 text/html pages have no .meta (the common path stays fast). // (FBS-82406 soft-404, FBS-82407 feed content-type + TTL) $xspeed_meta = array(); if ( file_exists( $xspeed_meta_file ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- pre-WP drop-in; one tiny JSON sidecar. $xspeed_meta_raw = file_get_contents( $xspeed_meta_file ); if ( false !== $xspeed_meta_raw ) { $xspeed_decoded = json_decode( $xspeed_meta_raw, true ); if ( is_array( $xspeed_decoded ) ) { $xspeed_meta = $xspeed_decoded; } } } // Per-content TTL from meta (e.g. feeds) falls back to the 24h page // default. HOUR_IN_SECONDS isn't defined yet (pre-WP), so use a literal. $xspeed_ttl = ( isset( $xspeed_meta['ttl'] ) && (int) $xspeed_meta['ttl'] > 0 ) ? (int) $xspeed_meta['ttl'] : 86400; $xspeed_age = time() - filemtime( $xspeed_cache_file ); if ( $xspeed_age < $xspeed_ttl ) { // PHP-served cache hit (the ~85ms fallback path). The nginx static // rewrite sends "HIT (nginx)" for the fast 5-15ms path; same header, // distinct value so you can tell which layer served the page. header( 'X-XSpeed-Cache: HIT (php)' ); // Record the HIT for the dashboard hit-ratio. The drop-in runs // BEFORE WordPress loads, so it can't call Hit_Counter — instead // it appends one line to the same hits.log the nginx static path // uses, and Hit_Counter::collect_nginx_log_hits() drains + counts // both on the next dashboard load. Without this, every drop-in HIT // was served but never counted, so the hit ratio sat at 0. // Best-effort: a failed append must never break serving the page. // // Path is baked in at install time by Cache::install_dropin(), which // replaces the @@XSPEED_HITS_LOG@@ token on the next line with the // resolved absolute path (uploads/xspeed/hits.log — NOT the cache dir, // which gets deleted on purge/uninstall and would take nginx down, // FBS-82478). The default below is the fallback for an un-substituted // drop-in (e.g. run straight from a dev source checkout); the installed // copy always carries the absolute uploads path. $xspeed_hits_log = '@@XSPEED_HITS_LOG@@'; // replaced at install if ( '@@' === substr( $xspeed_hits_log, 0, 2 ) ) { $xspeed_hits_log = WP_CONTENT_DIR . '/uploads/xspeed/hits.log'; } // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- pre-WP drop-in; WP_Filesystem isn't loaded. One short line, append + lock; failures are non-fatal (the ratio just under-counts). @file_put_contents( $xspeed_hits_log, "hit\n", FILE_APPEND | LOCK_EX ); // Replay the cached response's status + content-type from .meta, so a // cached 404 serves 404 (not a soft-404 200) and a cached feed serves // application/rss+xml (not text/html). (FBS-82406, FBS-82407) if ( ! empty( $xspeed_meta['status'] ) && function_exists( 'http_response_code' ) ) { http_response_code( (int) $xspeed_meta['status'] ); } if ( ! empty( $xspeed_meta['content_type'] ) && is_string( $xspeed_meta['content_type'] ) ) { header( 'Content-Type: ' . $xspeed_meta['content_type'] ); } // Conditional GET: Last-Modified + ETag from the cache file's mtime, // answer a matching If-Modified-Since / If-None-Match with 304 so // aggregators skip re-downloading an unchanged cached feed/page. // (FBS-82407 #5) $xspeed_mtime = (int) filemtime( $xspeed_cache_file ); if ( $xspeed_mtime > 0 ) { $xspeed_lastmod = gmdate( 'D, d M Y H:i:s', $xspeed_mtime ) . ' GMT'; $xspeed_etag = '"' . md5( $xspeed_cache_file . '|' . $xspeed_mtime ) . '"'; header( 'Last-Modified: ' . $xspeed_lastmod ); header( 'ETag: ' . $xspeed_etag ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- pre-WP drop-in; values only compared to a server-generated etag / parsed as a date, never echoed or executed. $xspeed_inm = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? trim( (string) $_SERVER['HTTP_IF_NONE_MATCH'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- as above. $xspeed_ims = isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ? trim( (string) $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) : ''; if ( ( '' !== $xspeed_inm && false !== strpos( $xspeed_inm, $xspeed_etag ) ) || ( '' !== $xspeed_ims && false !== ( $xspeed_ims_ts = strtotime( $xspeed_ims ) ) && $xspeed_ims_ts >= $xspeed_mtime ) ) { if ( function_exists( 'http_response_code' ) ) { http_response_code( 304 ); } exit; } } // Serve the precompressed Brotli sibling when the client accepts it // and the Pro Brotli module wrote .br. MUST mirror // XSpeed\Cache::maybe_serve_brotli() on the non-drop-in serve path — // both decide on the same Accept-Encoding token match + sibling // existence, so the response is identical whichever path serves. // pre-WP: no sanitize_text_field()/wp_unslash(); the value is only // lowercased + regex-matched, never echoed. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- pre-WP drop-in; value is only lowercased + token-matched, never echoed or executed. $xspeed_accept_enc = isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ? strtolower( str_replace( "\0", '', (string) $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) : ''; $xspeed_br_file = $xspeed_cache_file . '.br'; if ( preg_match( '/(^|[\s,])br([\s,;]|$)/', $xspeed_accept_enc ) && is_readable( $xspeed_br_file ) ) { header( 'Content-Encoding: br' ); header( 'Vary: Accept-Encoding', false ); header_remove( 'Content-Length' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Drop-in runs before WP_Filesystem is available; readfile streams the precompressed sibling directly. readfile( $xspeed_br_file ); exit; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Drop-in runs before WP_Filesystem is available; readfile is optimal for streaming a static cache file to the visitor. readfile( $xspeed_cache_file ); exit; } }