PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.7
1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 33 releases
← All changes | includes/class-hit-counter.php +37 -5 1.3.6 → 1.3.7 View file →
@@ -88,13 +88,20 @@
88 88 self::flush_pending();
89 89 }
90 90
91 91 /**
92 + * The bot / crawler / scanner alternation, without delimiters so the
93 + * drop-in can compose it — see excluded_ua_regex().
94 + */
95 + public const BOT_UA_PATTERN = 'bot|crawl|spider|slurp|scan|curl|wget|python-requests|python-urllib|libwww|httpclient|go-http|okhttp|axios|node-fetch|headless|phantomjs|masscan|nikto|sqlmap|zgrab|semrush|ahrefs|mj12|dotbot|petalbot|bytespider|facebookexternalhit|preview|monitor|uptime|pingdom|gtmetrix|lighthouse|pagespeed';
96 +
97 + /**
92 98 * Whether a User-Agent is a known bot / crawler / vulnerability scanner —
93 99 * its cache misses are cache-warming or hostile noise, not a signal of how
94 100 * the cache serves real visitors. Deliberately broad: matches the common
95 101 * crawler tokens plus the generic markers scanners and libraries carry.
96 - * Pure + unit-tested. (#118)
102 + * Unit-tested; no longer pure — Self_Traffic::is_self() runs the
103 + * xspeed_self_user_agents filter, so the answer can vary per site. (#118)
97 104 */
98 105 public static function is_bot_ua( string $ua ): bool {
99 106 if ( '' === $ua ) {
100 107 // No UA at all is overwhelmingly automated traffic, not a browser.
@@ -99,12 +106,37 @@
99 106 if ( '' === $ua ) {
100 107 // No UA at all is overwhelmingly automated traffic, not a browser.
101 108 return true;
102 109 }
103 - return 1 === preg_match(
104 - '~(bot|crawl|spider|slurp|scan|curl|wget|python-requests|python-urllib|libwww|httpclient|go-http|okhttp|axios|node-fetch|headless|phantomjs|masscan|nikto|sqlmap|zgrab|semrush|ahrefs|mj12|dotbot|petalbot|bytespider|facebookexternalhit|preview|monitor|uptime|pingdom|gtmetrix|lighthouse|pagespeed)~i',
105 - $ua
106 - );
110 + // Our own warmer, benchmark and verifier are warming the cache, not
111 + // visiting it: `xSpeed-Warmer`, `xSpeed Benchmark`, and the rest.
112 + // Callers with a request also check Self_Traffic::request_is_marked().
113 + if ( Self_Traffic::is_self( $ua ) ) {
114 + return true;
115 + }
116 + return 1 === preg_match( '~(' . self::BOT_UA_PATTERN . ')~i', $ua );
117 + }
118 +
119 + /**
120 + * The "do not count this user agent" alternation: bots and scanners,
121 + * plus the fragments xSpeed's own requests carry. A renamed warmer is
122 + * not in it on purpose; that request is recognised by
123 + * Self_Traffic::HEADER, because its UA may be a real browser's.
124 + *
125 + * Baked into the drop-in at install time (`@@XSPEED_HIT_EXCLUDE_RE@@`).
126 + * The drop-in runs before WordPress, so it cannot ask this class and the
127 + * hits.log line it writes carries no user agent — nothing downstream can
128 + * reclassify the line later, which is why the decision has to travel
129 + * with the file. A hardcoded copy of the fragments drifted instead: it
130 + * excluded the warmer but still counted every crawler HIT, and it could
131 + * not know about an overridden `xspeed_preloader_user_agent`.
132 + */
133 + public static function excluded_ua_regex(): string {
134 + $parts = array( self::BOT_UA_PATTERN );
135 + foreach ( Self_Traffic::agents() as $agent ) {
136 + $parts[] = preg_quote( $agent, '#' );
137 + }
138 + return implode( '|', $parts );
107 139 }
108 140
109 141 public static function record_miss(): void {
110 142 ++self::$pending['miss'];