PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.2
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 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
xspeed / includes / class-browser-cache.php

class-browser-cache.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.0.2, at includes/class-browser-cache.php

118 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Browser_Cache — writes/removes browser-cache directives in the site
4 * root .htaccess so static assets get long Cache-Control + Expires
5 * headers, and serves an nginx snippet for non-Apache hosts.
6 *
7 * Same shape as Gzip: marker block, insert_with_markers, snippet
8 * fallback. Independent toggle so users can enable browser caching
9 * without compression and vice versa.
10 *
11 * The default TTLs follow LiteSpeed/WP Rocket conventions:
12 * - Static assets (CSS/JS/fonts/images): 1 year + immutable.
13 * - HTML: 1 hour (so post edits go live the same day even if a CDN
14 * has cached the document).
15 *
16 * @package XSpeed
17 */
18
19 declare(strict_types=1);
20
21 namespace XSpeed;
22
23 defined( 'ABSPATH' ) || exit;
24
25 final class Browser_Cache {
26
27 public const MARKER = 'xSpeed Browser Cache';
28
29 public const DEFAULT_ASSET_TTL = 31536000; // 1 year
30 public const DEFAULT_HTML_TTL = 3600; // 1 hour
31
32 public static function apply( bool $enabled, array $opts = array() ): bool {
33 if ( ! function_exists( 'XSpeed\\Server::supports_htaccess' ) && class_exists( '\\XSpeed\\Server' ) ) {
34 if ( ! Server::supports_htaccess() ) {
35 return false;
36 }
37 }
38 $htaccess = ABSPATH . '.htaccess';
39 if ( ! file_exists( $htaccess ) ) {
40 return false;
41 }
42 if ( ! function_exists( 'insert_with_markers' ) ) {
43 require_once ABSPATH . 'wp-admin/includes/misc.php';
44 }
45 $rules = $enabled ? self::apache_rules( $opts ) : array();
46 return (bool) insert_with_markers( $htaccess, self::MARKER, $rules );
47 }
48
49 /**
50 * Apache rule lines. mod_expires handles the Expires header; an
51 * inline mod_headers Cache-Control mirror lets us include
52 * `immutable` (mod_expires doesn't emit it).
53 *
54 * @return string[]
55 */
56 public static function apache_rules( array $opts = array() ): array {
57 $asset = (int) ( $opts['asset_ttl'] ?? self::DEFAULT_ASSET_TTL );
58 $html = (int) ( $opts['html_ttl'] ?? self::DEFAULT_HTML_TTL );
59 if ( $asset < 0 ) {
60 $asset = self::DEFAULT_ASSET_TTL;
61 }
62 if ( $html < 0 ) {
63 $html = self::DEFAULT_HTML_TTL;
64 }
65 return array(
66 '<IfModule mod_expires.c>',
67 ' ExpiresActive On',
68 ' ExpiresByType text/html "access plus ' . $html . ' seconds"',
69 ' ExpiresByType text/css "access plus ' . $asset . ' seconds"',
70 ' ExpiresByType text/javascript "access plus ' . $asset . ' seconds"',
71 ' ExpiresByType application/javascript "access plus ' . $asset . ' seconds"',
72 ' ExpiresByType application/json "access plus ' . $html . ' seconds"',
73 ' ExpiresByType image/jpeg "access plus ' . $asset . ' seconds"',
74 ' ExpiresByType image/png "access plus ' . $asset . ' seconds"',
75 ' ExpiresByType image/webp "access plus ' . $asset . ' seconds"',
76 ' ExpiresByType image/avif "access plus ' . $asset . ' seconds"',
77 ' ExpiresByType image/gif "access plus ' . $asset . ' seconds"',
78 ' ExpiresByType image/svg+xml "access plus ' . $asset . ' seconds"',
79 ' ExpiresByType image/x-icon "access plus ' . $asset . ' seconds"',
80 ' ExpiresByType font/woff2 "access plus ' . $asset . ' seconds"',
81 ' ExpiresByType font/woff "access plus ' . $asset . ' seconds"',
82 ' ExpiresByType font/ttf "access plus ' . $asset . ' seconds"',
83 ' ExpiresByType font/otf "access plus ' . $asset . ' seconds"',
84 '</IfModule>',
85 '<IfModule mod_headers.c>',
86 ' <FilesMatch "\.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2|woff|ttf|otf|eot|mp4|webm|mp3|ogg)$">',
87 ' Header set Cache-Control "public, max-age=' . $asset . ', immutable"',
88 ' </FilesMatch>',
89 ' <FilesMatch "\.html$">',
90 ' Header set Cache-Control "public, max-age=' . $html . '"',
91 ' </FilesMatch>',
92 '</IfModule>',
93 );
94 }
95
96 /**
97 * nginx snippet — uses `expires` directive (the canonical nginx way)
98 * plus an `add_header` line for the immutable flag.
99 */
100 public static function nginx_snippet( array $opts = array() ): string {
101 $asset = (int) ( $opts['asset_ttl'] ?? self::DEFAULT_ASSET_TTL );
102 $html = (int) ( $opts['html_ttl'] ?? self::DEFAULT_HTML_TTL );
103 return implode(
104 "\n",
105 array(
106 'location ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2|woff|ttf|otf|eot|mp4|webm|mp3|ogg)$ {',
107 ' expires ' . $asset . 's;',
108 ' add_header Cache-Control "public, max-age=' . $asset . ', immutable";',
109 '}',
110 'location ~* \.html$ {',
111 ' expires ' . $html . 's;',
112 ' add_header Cache-Control "public, max-age=' . $html . '";',
113 '}',
114 )
115 );
116 }
117 }
118