' . "\n"; $rules .= "ExpiresActive On\n"; $rules .= "ExpiresByType image/jpeg \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/gif \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/png \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/webp \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/svg+xml \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/x-icon \"access plus 1 year\"\n"; $rules .= "ExpiresByType image/avif \"access plus 1 year\"\n"; $rules .= "ExpiresByType video/mp4 \"access plus 1 month\"\n"; $rules .= "ExpiresByType video/webm \"access plus 1 month\"\n"; $rules .= "ExpiresByType audio/mpeg \"access plus 1 month\"\n"; $rules .= "ExpiresByType audio/ogg \"access plus 1 month\"\n"; $rules .= "ExpiresByType text/css \"access plus 1 year\"\n"; $rules .= "ExpiresByType text/javascript \"access plus 1 year\"\n"; $rules .= "ExpiresByType application/javascript \"access plus 1 year\"\n"; $rules .= "ExpiresByType font/woff2 \"access plus 1 year\"\n"; $rules .= "ExpiresByType font/woff \"access plus 1 year\"\n"; $rules .= "ExpiresByType application/font-woff \"access plus 1 year\"\n"; $rules .= "ExpiresByType application/font-woff2 \"access plus 1 year\"\n"; $rules .= "ExpiresByType application/vnd.ms-fontobject \"access plus 1 year\"\n"; $rules .= "ExpiresByType application/x-font-ttf \"access plus 1 year\"\n"; $rules .= '' . "\n"; $rules .= '# END WP Caching Out Expires'; return $rules; } /** * Generate the GZIP compression .htaccess block. * * @since 0.1.0 * @return string */ public static function get_gzip_rules(): string { $rules = '# BEGIN WP Caching Out GZIP' . "\n"; $rules .= '' . "\n"; $rules .= "AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json application/xml text/xml application/rss+xml image/svg+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject\n"; $rules .= '' . "\n"; $rules .= "BrowserMatch ^Mozilla/4 gzip-only-text/html\n"; $rules .= "BrowserMatch ^Mozilla/4\\.0[678] no-gzip\n"; $rules .= "BrowserMatch \\bMSIE !no-gzip !gzip-only-text/html\n"; $rules .= '' . "\n"; $rules .= '' . "\n"; $rules .= '# END WP Caching Out GZIP'; return $rules; } /** * Generate the Brotli compression .htaccess block. * * @since 0.1.0 * @return string */ public static function get_brotli_rules(): string { $rules = '# BEGIN WP Caching Out Brotli' . "\n"; $rules .= '' . "\n"; $rules .= "AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/javascript application/javascript application/json application/xml text/xml application/rss+xml image/svg+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject\n"; $rules .= '' . "\n"; $rules .= '# END WP Caching Out Brotli'; return $rules; } /** * Generate a documented NGINX snippet. * * Never written to server config programmatically — displayed in * the admin UI as a read-only block for manual inclusion. * * @since 0.1.0 * @return string */ public static function get_nginx_snippet(): string { $rules = "# ---- WP Caching Out: Browser Caching & Compression ----\n"; $rules .= "# Add this to your server block (or a location block).\n"; $rules .= "#\n"; $rules .= "# 1. Far-future Expires / Cache-Control for static assets.\n"; $rules .= "location ~* \\.(css|js|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot|ico|xml|mp4|webm|ogg)\$ {\n"; $rules .= " expires 1y;\n"; $rules .= " add_header Cache-Control \"public, immutable\";\n"; $rules .= "}\n"; $rules .= "\n"; $rules .= "# 2. GZIP compression for text-based assets.\n"; $rules .= "gzip on;\n"; $rules .= "gzip_types text/html text/css text/javascript application/javascript application/json application/xml text/xml application/rss+xml image/svg+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject;\n"; $rules .= "\n"; $rules .= "# 3. Brotli compression (requires brotli module).\n"; $rules .= "# brotli on;\n"; $rules .= "# brotli_types text/html text/css text/javascript application/javascript application/json application/xml text/xml application/rss+xml image/svg+xml;\n"; $rules .= "# ---- End WP Caching Out ----"; return $rules; } /** * Write all enabled browser-cache blocks to .htaccess. * * @since 0.1.0 * * @param CachingOut_Settings $settings Settings instance. * @return true|\WP_Error */ public function write( CachingOut_Settings $settings ) { $expires = $settings->get( 'browser_cache', 'expires', true ); $gzip = $settings->get( 'browser_cache', 'gzip', true ); $brotli = $settings->get( 'browser_cache', 'brotli', true ); $result = CachingOut_Activator::update_htaccess_block( self::MARKER_EXPIRES, $expires ? self::get_expires_rules() : null ); if ( is_wp_error( $result ) ) { return $result; } $result = CachingOut_Activator::update_htaccess_block( self::MARKER_GZIP, $gzip ? self::get_gzip_rules() : null ); if ( is_wp_error( $result ) ) { return $result; } return CachingOut_Activator::update_htaccess_block( self::MARKER_BROTLI, $brotli ? self::get_brotli_rules() : null ); } /** * Remove all Caching Out browser-cache blocks from .htaccess. * * Used during deactivation. Removes all three blocks regardless of * current settings. * * @since 0.1.0 * @return true|\WP_Error */ public function remove() { $errors = array(); foreach ( array( self::MARKER_EXPIRES, self::MARKER_GZIP, self::MARKER_BROTLI ) as $marker ) { $result = CachingOut_Activator::update_htaccess_block( $marker, null ); if ( is_wp_error( $result ) ) { $errors[] = $result; } } if ( ! empty( $errors ) ) { $combined = new \WP_Error( 'cachingout_browser_cache_remove_partial' ); foreach ( $errors as $error ) { $combined->add( $error->get_error_code(), $error->get_error_message() ); } return $combined; } return true; } }