@@ -82,6 +82,12 @@ public static function is_available( $force = false ) {
8282 * PURGE requests must be sent to localhost (127.0.0.1) with the Host header
8383 * set to the actual domain. This is how ea-nginx-cache-purge is configured.
8484 *
85+ * The module returns specific signatures in the response body:
86+ * - HTTP 200 with body containing "Successful purge" = module working.
87+ * - HTTP 412 can occur when item not in cache, but we need body signature.
88+ *
89+ * Without the module, nginx may return 301/404/405 with generic HTML.
90+ *
8591 * @return bool True if PURGE is supported.
8692 */
8793 private static function test_purge_capability () {
@@ -111,36 +117,40 @@ private static function test_purge_capability() {
111117 $ code = wp_remote_retrieve_response_code ( $ response );
112118 $ body = wp_remote_retrieve_body ( $ response );
113119
114- // ea-nginx-cache-purge returns:
115- // - 200 OK with "Successful purge" if entry existed.
116- // - 404 Not Found if entry didn't exist (but PURGE is supported).
117- // - 412 Precondition Failed if entry wasn't in cache.
118- // - 405 Method Not Allowed if PURGE is not configured.
119- if ( 200 === $ code || 404 === $ code || 412 === $ code ) {
120- // Check for cache-purge signature in body.
121- if ( stripos ( $ body , 'purge ' ) !== false || stripos ( $ body , 'cache ' ) !== false ) {
122- self ::log ( 'PURGE test successful: module detected ' );
123- return true ;
124- }
125- // Even without body, 200/404/412 on PURGE is a good sign.
126- self ::log ( 'PURGE test: got ' . $ code . ', assuming module available ' );
120+ // ea-nginx-cache-purge module returns specific signatures:
121+ // - 200 OK with body "Successful purge" and "Key :" when purged.
122+ // - 412 Precondition Failed when item wasn't in cache.
123+ //
124+ // Without module: 301 redirect, 404 generic HTML, or 405 Method Not Allowed.
125+ //
126+ // We MUST check body for "Successful purge" signature to confirm module.
127+ if ( 200 === $ code && stripos ( $ body , 'Successful purge ' ) !== false ) {
128+ self ::log ( 'PURGE test successful: module detected (200 + signature) ' );
129+ return true ;
130+ }
131+
132+ // 412 from module still indicates it's working (item just wasn't cached).
133+ // But generic nginx 412 doesn't have our signature, so check for module output.
134+ if ( 412 === $ code && stripos ( $ body , 'Precondition Failed ' ) !== false ) {
135+ // This is likely the module - generic nginx rarely returns 412 for PURGE.
136+ self ::log ( 'PURGE test successful: module detected (412 Precondition Failed) ' );
127137 return true ;
128138 }
129139
130- self ::log ( 'PURGE test: got ' . $ code . ', module not available ' );
140+ self ::log ( 'PURGE test: got HTTP ' . $ code . ', module not detected ' );
131141 return false ;
132142 }
133143
134144 /**
135145 * Check if selective purging is enabled.
136146 *
137- * @return bool True if enabled and available.
147+ * Returns true if the user has enabled selective purging via checkbox.
148+ * Module availability is checked separately during actual purge operations.
149+ *
150+ * @return bool True if enabled by user.
138151 */
139152 public static function is_enabled () {
140- $ enabled = get_option ( self ::OPTION_ENABLED , 'yes ' );
141- $ available = get_option ( self ::OPTION_AVAILABLE , 'unknown ' );
142-
143- return 'yes ' === $ enabled && 'yes ' === $ available ;
153+ return 'yes ' === get_option ( self ::OPTION_ENABLED , 'no ' );
144154 }
145155
146156 /**
@@ -207,21 +217,54 @@ public static function purge_url( $url ) {
207217 }
208218
209219 $ code = wp_remote_retrieve_response_code ( $ response );
220+ $ body = wp_remote_retrieve_body ( $ response );
210221
211- // 200 = successfully purged, 404 = wasn't in cache (ok), 412 = wasn't in cache.
212- if ( 200 === $ code || 404 === $ code || 412 === $ code ) {
213- self ::log ( 'Purged: ' . $ url . ' (HTTP ' . $ code . ') ' );
222+ // ea-nginx-cache-purge module returns:
223+ // - 200 with "Successful purge" = item was in cache and purged.
224+ // - 412 Precondition Failed = item wasn't in cache (still success).
225+ //
226+ // Without module: 301/404/405 with generic nginx HTML (not a real purge).
227+ if ( 200 === $ code && stripos ( $ body , 'Successful purge ' ) !== false ) {
228+ self ::log ( 'Purged: ' . $ url . ' (HTTP 200) ' );
214229 return array (
215230 'success ' => true ,
216231 'message ' => 'Purged successfully ' ,
217232 'code ' => $ code ,
218233 );
219234 }
220235
236+ // 412 means item wasn't cached - that's fine, nothing to purge.
237+ if ( 412 === $ code ) {
238+ self ::log ( 'Not in cache: ' . $ url . ' (HTTP 412) ' );
239+ return array (
240+ 'success ' => true ,
241+ 'message ' => 'Not in cache (already clean) ' ,
242+ 'code ' => $ code ,
243+ );
244+ }
245+
246+ // Got a 200 but without cache-purge signature = module not working.
247+ if ( 200 === $ code ) {
248+ self ::log ( 'Purge returned 200 but without module signature for ' . $ url );
249+ return array (
250+ 'success ' => false ,
251+ 'message ' => 'Cache purge module not responding correctly ' ,
252+ 'code ' => $ code ,
253+ );
254+ }
255+
256+ // 301/404/405 = module not installed or not configured for this domain.
257+ $ error_msg = 'Purge failed (HTTP ' . $ code . ') ' ;
258+ if ( 404 === $ code || 301 === $ code ) {
259+ $ error_msg = 'Cache purge module not detected - install ea-nginx-cache-purge ' ;
260+ } elseif ( 405 === $ code ) {
261+ $ error_msg = 'PURGE method not allowed - cache purge module not configured ' ;
262+ }
263+
221264 self ::log ( 'Purge failed for ' . $ url . ': HTTP ' . $ code );
222265 return array (
223266 'success ' => false ,
224- 'message ' => ' Unexpected response: HTTP ' . $ code ,
267+ 'message ' => $ error_msg ,
225268 'code ' => $ code ,
226269 );
227270 }
@@ -466,28 +509,38 @@ private static function log( $message ) {
466509 /**
467510 * Get availability status for display.
468511 *
512+ * Detection only runs on settings page (force_recheck=true) to avoid
513+ * HTTP requests on every page load. Admin bar uses cached result.
514+ *
515+ * @param bool $force_recheck Force a fresh detection check (use on settings page).
469516 * @return array Status info with 'available', 'enabled', 'message' keys.
470517 */
471- public static function get_status () {
518+ public static function get_status ( $ force_recheck = false ) {
519+ $ enabled = self ::is_enabled ();
472520 $ available = get_option ( self ::OPTION_AVAILABLE , 'unknown ' );
473- $ enabled = get_option ( self ::OPTION_ENABLED , 'yes ' );
474521
475- if ( 'unknown ' === $ available ) {
476- $ available = self ::is_available () ? 'yes ' : 'no ' ;
522+ // Only recheck on settings page (force_recheck=true).
523+ // This avoids HTTP requests on every page load.
524+ if ( $ force_recheck && $ enabled ) {
525+ $ available = self ::is_available ( true ) ? 'yes ' : 'no ' ;
477526 }
478527
479528 $ message = '' ;
480- if ( 'yes ' === $ available && 'yes ' === $ enabled ) {
481- $ message = 'Selective purging active — only changed pages are purged for maximum efficiency ' ;
482- } elseif ( 'yes ' === $ available && 'no ' === $ enabled ) {
483- $ message = 'Selective purging available but disabled ' ;
529+ if ( $ enabled ) {
530+ if ( 'yes ' === $ available ) {
531+ $ message = 'Selective purging active — only changed pages are purged for maximum efficiency ' ;
532+ } elseif ( 'no ' === $ available ) {
533+ $ message = 'Selective purging enabled but module not detected — purges may fail ' ;
534+ } else {
535+ $ message = 'Selective purging enabled — checking module availability... ' ;
536+ }
484537 } else {
485- $ message = 'nginx-module-cache-purge not detected — using full cache purge via cPanel API ' ;
538+ $ message = 'Selective purging disabled — using full cache purge via cPanel API ' ;
486539 }
487540
488541 return array (
489542 'available ' => 'yes ' === $ available ,
490- 'enabled ' => ' yes ' === $ enabled ,
543+ 'enabled ' => $ enabled ,
491544 'message ' => $ message ,
492545 );
493546 }
0 commit comments