# xspeed/1.0.2/includes/class-browser-cache.php

xSpeed Cache: AI-Powered Performance Hub with MCP, Caching &amp; CDN, version 1.0.2. 118 lines.

- Page: https://pluginprobe.com/plugins/xspeed/1.0.2/code/includes/class-browser-cache.php
- Raw: https://pluginprobe.com/plugins/xspeed/1.0.2/raw/includes/class-browser-cache.php
- Modified: 2026-06-01T17:33:22+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/xspeed/1.0.2/code/includes/class-browser-cache.php#L10-L20`.

```php
<?php
/**
 * Browser_Cache — writes/removes browser-cache directives in the site
 * root .htaccess so static assets get long Cache-Control + Expires
 * headers, and serves an nginx snippet for non-Apache hosts.
 *
 * Same shape as Gzip: marker block, insert_with_markers, snippet
 * fallback. Independent toggle so users can enable browser caching
 * without compression and vice versa.
 *
 * The default TTLs follow LiteSpeed/WP Rocket conventions:
 *   - Static assets (CSS/JS/fonts/images): 1 year + immutable.
 *   - HTML: 1 hour (so post edits go live the same day even if a CDN
 *     has cached the document).
 *
 * @package XSpeed
 */

declare(strict_types=1);

namespace XSpeed;

defined( 'ABSPATH' ) || exit;

final class Browser_Cache {

	public const MARKER = 'xSpeed Browser Cache';

	public const DEFAULT_ASSET_TTL = 31536000; // 1 year
	public const DEFAULT_HTML_TTL  = 3600;     // 1 hour

	public static function apply( bool $enabled, array $opts = array() ): bool {
		if ( ! function_exists( 'XSpeed\\Server::supports_htaccess' ) && class_exists( '\\XSpeed\\Server' ) ) {
			if ( ! Server::supports_htaccess() ) {
				return false;
			}
		}
		$htaccess = ABSPATH . '.htaccess';
		if ( ! file_exists( $htaccess ) ) {
			return false;
		}
		if ( ! function_exists( 'insert_with_markers' ) ) {
			require_once ABSPATH . 'wp-admin/includes/misc.php';
		}
		$rules = $enabled ? self::apache_rules( $opts ) : array();
		return (bool) insert_with_markers( $htaccess, self::MARKER, $rules );
	}

	/**
	 * Apache rule lines. mod_expires handles the Expires header; an
	 * inline mod_headers Cache-Control mirror lets us include
	 * `immutable` (mod_expires doesn't emit it).
	 *
	 * @return string[]
	 */
	public static function apache_rules( array $opts = array() ): array {
		$asset = (int) ( $opts['asset_ttl'] ?? self::DEFAULT_ASSET_TTL );
		$html  = (int) ( $opts['html_ttl'] ?? self::DEFAULT_HTML_TTL );
		if ( $asset < 0 ) {
			$asset = self::DEFAULT_ASSET_TTL;
		}
		if ( $html < 0 ) {
			$html = self::DEFAULT_HTML_TTL;
		}
		return array(
			'<IfModule mod_expires.c>',
			'  ExpiresActive On',
			'  ExpiresByType text/html "access plus ' . $html . ' seconds"',
			'  ExpiresByType text/css "access plus ' . $asset . ' seconds"',
			'  ExpiresByType text/javascript "access plus ' . $asset . ' seconds"',
			'  ExpiresByType application/javascript "access plus ' . $asset . ' seconds"',
			'  ExpiresByType application/json "access plus ' . $html . ' seconds"',
			'  ExpiresByType image/jpeg "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/png "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/webp "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/avif "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/gif "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/svg+xml "access plus ' . $asset . ' seconds"',
			'  ExpiresByType image/x-icon "access plus ' . $asset . ' seconds"',
			'  ExpiresByType font/woff2 "access plus ' . $asset . ' seconds"',
			'  ExpiresByType font/woff "access plus ' . $asset . ' seconds"',
			'  ExpiresByType font/ttf "access plus ' . $asset . ' seconds"',
			'  ExpiresByType font/otf "access plus ' . $asset . ' seconds"',
			'</IfModule>',
			'<IfModule mod_headers.c>',
			'  <FilesMatch "\.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2|woff|ttf|otf|eot|mp4|webm|mp3|ogg)$">',
			'    Header set Cache-Control "public, max-age=' . $asset . ', immutable"',
			'  </FilesMatch>',
			'  <FilesMatch "\.html$">',
			'    Header set Cache-Control "public, max-age=' . $html . '"',
			'  </FilesMatch>',
			'</IfModule>',
		);
	}

	/**
	 * nginx snippet — uses `expires` directive (the canonical nginx way)
	 * plus an `add_header` line for the immutable flag.
	 */
	public static function nginx_snippet( array $opts = array() ): string {
		$asset = (int) ( $opts['asset_ttl'] ?? self::DEFAULT_ASSET_TTL );
		$html  = (int) ( $opts['html_ttl'] ?? self::DEFAULT_HTML_TTL );
		return implode(
			"\n",
			array(
				'location ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2|woff|ttf|otf|eot|mp4|webm|mp3|ogg)$ {',
				'    expires ' . $asset . 's;',
				'    add_header Cache-Control "public, max-age=' . $asset . ', immutable";',
				'}',
				'location ~* \.html$ {',
				'    expires ' . $html . 's;',
				'    add_header Cache-Control "public, max-age=' . $html . '";',
				'}',
			)
		);
	}
}

```
