RewriteEngine On RewriteCond %{HTTP:Accept} application RewriteRule ^ - [E=Cache-Control:vary=%{ENV:LSCACHE_VARY_VALUE}+isjson] '; /** * The option name to store the htaccess rules. * * @var string */ public static $option_name = 'activitypub_litespeed_cache_setup'; /** * The marker to identify the rules in the htaccess file. * * @var string */ public static $marker = 'ActivityPub LiteSpeed Cache'; /** * The LiteSpeed Cache plugin slug. * * @var string */ public static $plugin_slug = 'litespeed-cache/litespeed-cache.php'; /** * Initialize the integration. */ public static function init() { // Add rules if LiteSpeed Cache is active and rules aren't set. if ( is_plugin_active( self::$plugin_slug ) ) { if ( ! \get_option( self::$option_name ) ) { self::add_htaccess_rules(); } \add_filter( 'site_status_tests', array( self::class, 'add_site_health_test' ) ); // Remove rules if LiteSpeed Cache is not active but rules were previously set. } elseif ( \get_option( self::$option_name ) ) { self::remove_htaccess_rules(); } // Clean up when LiteSpeed Cache plugin is deleted. \add_action( 'deleted_plugin', array( self::class, 'on_plugin_deleted' ) ); } /** * Clean up htaccess rules when LiteSpeed Cache plugin is deleted. * * @param string $plugin_file Path to the plugin file relative to the plugins directory. */ public static function on_plugin_deleted( $plugin_file ) { if ( self::$plugin_slug === $plugin_file && \get_option( self::$option_name ) ) { self::remove_htaccess_rules(); } } /** * Add the LiteSpeed Cache htaccess rules. */ public static function add_htaccess_rules() { $added_rules = self::append_with_markers( self::$marker, self::$rules ); if ( $added_rules ) { \update_option( self::$option_name, '1' ); } else { \update_option( self::$option_name, '0' ); } } /** * Remove the LiteSpeed Cache htaccess rules. */ public static function remove_htaccess_rules() { self::append_with_markers( self::$marker, '' ); \delete_option( self::$option_name ); } /** * Add the LiteSpeed Cache config test to site health. * * @param array $tests The site health tests. * * @return array The site health tests with the LiteSpeed Cache config test. */ public static function add_site_health_test( $tests ) { $tests['direct']['activitypub_test_litespeed_cache_integration'] = array( 'label' => \__( 'LiteSpeed Cache Test', 'activitypub' ), 'test' => array( self::class, 'test_litespeed_cache_integration' ), ); return $tests; } /** * Test the LiteSpeed Cache integration. * * @return array The test results. */ public static function test_litespeed_cache_integration() { $result = array( 'label' => \__( 'Compatibility with LiteSpeed Cache', 'activitypub' ), 'status' => 'good', 'badge' => array( 'label' => \__( 'ActivityPub', 'activitypub' ), 'color' => 'green', ), 'description' => \sprintf( '
%s
', \__( 'LiteSpeed Cache is well configured to work with ActivityPub.', 'activitypub' ) ), 'actions' => '', 'test' => 'test_litespeed_cache_integration', ); if ( ! \get_option( self::$option_name ) ) { $result['status'] = 'critical'; $result['label'] = \__( 'LiteSpeed Cache might not be properly configured.', 'activitypub' ); $result['badge']['color'] = 'red'; $result['description'] = \sprintf( '%s
', \__( 'LiteSpeed Cache isn’t currently set up to work with ActivityPub. While this isn’t a major problem, it’s a good idea to enable support. Without it, some technical files (like JSON) might accidentally show up in your website’s cache and be visible to visitors.', 'activitypub' ) ); $result['actions'] = \sprintf( '%s
%s', \__( 'To enable the ActivityPub integration with LiteSpeed Cache, add the following rules to your
.htaccess file:', 'activitypub' ),
\esc_html( self::$rules )
);
}
return $result;
}
/**
* Prepend rules to the top of a file with markers.
*
* @param string $marker The marker to identify the rules in the file.
* @param string $rules The rules to prepend.
*
* @return bool True on success, false on failure.
*/
private static function append_with_markers( $marker, $rules ) {
$htaccess_file = self::get_htaccess_file_path();
if ( ! \wp_is_writable( $htaccess_file ) ) {
return false;
}
// Ensure WP_Filesystem() is declared.
require_once ABSPATH . 'wp-admin/includes/file.php';
global $wp_filesystem;
\WP_Filesystem();
$htaccess = $wp_filesystem->get_contents( $htaccess_file );
// If marker exists, remove the old block first.
if ( strpos( $htaccess, $marker ) !== false ) {
// Remove existing marker block.
$pattern = '/# BEGIN ' . preg_quote( $marker, '/' ) . '.*?# END ' . preg_quote( $marker, '/' ) . '\r?\n?/s';
$htaccess = preg_replace( $pattern, '', $htaccess );
$htaccess = trim( $htaccess );
}
// If rules are empty, just return (for removal case).
if ( empty( $rules ) ) {
return $wp_filesystem->put_contents( $htaccess_file, $htaccess, FS_CHMOD_FILE );
}
// Prepend new rules to the top of the file.
$start_marker = "# BEGIN {$marker}";
$end_marker = "# END {$marker}";
$rules = $start_marker . PHP_EOL . $rules . PHP_EOL . $end_marker;
$htaccess = $rules . PHP_EOL . PHP_EOL . $htaccess;
return $wp_filesystem->put_contents( $htaccess_file, $htaccess, FS_CHMOD_FILE );
}
/**
* Get the htaccess file.
*
* @return string|false The htaccess file or false.
*/
private static function get_htaccess_file_path() {
$htaccess_file = false;
// Ensure get_home_path() is declared.
require_once ABSPATH . 'wp-admin/includes/file.php';
// phpcs:ignore WordPress.PHP.NoSilencedErrors
if ( @file_exists( \get_home_path() . '.htaccess' ) ) {
/** The htaccess file resides in ABSPATH */
$htaccess_file = \get_home_path() . '.htaccess';
}
/**
* Filter the htaccess file path.
*
* @param string|false $htaccess_file The htaccess file path.
*/
return \apply_filters( 'activitypub_litespeed_cache_htaccess_file', $htaccess_file );
}
}