WP Rocket: cache invalidation from third-party code
For developers shipping a plugin or theme that mutates content WP Rocket has cached — saving a custom CPT, completing a WooCommerce order, importing data, processing a webhook, scheduling a bulk update. WP Rocket caches HTML to disk (NOT to the WP object cache); raw cache invalidation requires going through the plugin's public API, otherwise stale HTML stays served until the cache TTL or the next save.
WP Rocket is a paid plugin, not on Packagist, not in the WordPress.org plugin directory. Many sites have it; many don't. Every code path that touches
rocket_clean_*MUST be feature-detected first — otherwise your code fatals on installs without WP Rocket.
Misconception this skill corrects
"I'll call
wp_cache_flush()after my plugin saves data — that clears WP Rocket too."
It doesn't. wp_cache_flush() clears the WordPress object cache (transients, options, post meta caches in Redis / Memcached / WP_Cache_Object). WP Rocket is a page cache that writes static HTML files to disk under wp-content/cache/wp-rocket/.... The two are completely independent layers — you can flush the object cache 1000 times and the WP Rocket cached HTML stays untouched.
The right entry point: rocket_clean_post( $post_id ) if a specific post changed, or one of the more granular rocket_clean_* functions for other scenarios. Verified at wp-content/plugins/wp-rocket/inc/common/purge.php:167 and inc/functions/files.php.
Other AI-prone misconceptions:
- "I'll just
unlink()the WP Rocket cache files for this URL." Wrong direction — WP Rocket's filename layout is non-trivial: desktop / mobile / tablet variants, language variants (/cache/wp-rocket/example.com-en/...), query-string variants, gzipped variants, webp variants. Manual deletion misses some, leaves stale files, AND skips thebefore_/after_action hooks that other plugins (CDN purgers, Varnish, Cloudflare addons) listen for. - "
function_exists('rocket_clean_post')is paranoid; everyone has WP Rocket." No, WP Rocket is paid. ~30% of WP installs use SOME caching plugin; even of those, WP Rocket is one of many. Always feature-detect. - "
is_plugin_active('wp-rocket/wp-rocket.php')" is the right check." Half-true.is_plugin_active()requireswp-admin/includes/plugin.phpto be loaded — it's NOT available during early hooks likeplugins_loaded.defined('WP_ROCKET_VERSION')andfunction_exists('rocket_clean_post')work everywhere.
When to use this skill
Trigger when ANY of the following is true:
- The diff calls
rocket_clean_*,wp_cache_flush(),rocket_*cache operations, OR raw filesystem operations againstwp-content/cache/wp-rocket/. - A plugin saves data and the user expects the cached page to refresh.
- WooCommerce / membership / LMS plugin invalidation flows (e.g. user enrolls in a course → cached course page shows old "no access" state).
- Reviewing PR code that hooks
save_post/transition_post_status/woocommerce_*events and wants to invalidate cache. - Custom CPT integration where post-save → page invalidation is needed.
- Bulk import / migration scripts that should NOT churn the cache during the import (delay invalidation to the end).
Plugin identity (verified)
| Field | Value |
|---|---|
| Plugin | WP Rocket |
| Version | 3.21.1 (Code name "Iego") |
| Min WP | 5.8 |
| Min PHP | 7.3 |
| Tested up to | WP 6.3.1 |
| Distribution | Paid / premium — not on Packagist, not on WP.org repo |
| Constants | WP_ROCKET_VERSION, WP_ROCKET_SLUG = 'wp_rocket_settings', WP_ROCKET_PHP_VERSION, WP_ROCKET_WP_VERSION |
| Text domain | rocket |
| Cache type | File-based page cache (HTML written to disk) — NOT object cache |
Public cache-invalidation API (verified)
All functions live in wp-content/plugins/wp-rocket/inc/functions/files.php except rocket_clean_post which is in inc/common/purge.php.
| Function | Args | What it clears |
|---|---|---|
rocket_clean_post( $post_id, $post = null ) |
int post ID, optional WP_Post | Cached HTML for this post + its archive(s) + home (per built-in logic) |
rocket_clean_files( $urls, $filesystem = null, $run_actions = true ) |
string or array of URLs | Cached HTML for arbitrary URLs (e.g. category archives, custom permalinks) |
rocket_clean_term( $term_id, $taxonomy_slug ) |
int + string | Taxonomy archive page |
rocket_clean_user( $user_id, $lang = '' ) |
int + optional lang code | User-specific cache (logged-in user dynamic cookies) |
rocket_clean_home( $lang = '' ) |
optional lang code | Homepage cache only |
rocket_clean_home_feeds() |
none | Home feed cache |
rocket_clean_domain( $lang = '', $filesystem = null ) |
optional lang + filesystem | Everything for the domain — the nuke option |
rocket_clean_cache_dir() |
none | The entire cache directory (across domains on multisite) |
rocket_clean_minify( $extensions = ['js', 'css'] ) |
array of extensions | Minified asset cache |
rocket_clean_cache_busting( $extensions = ['js', 'css'] ) |
array of extensions | Cache-busting versioned static asset files |
Workflow
1. Always feature-detect first
if ( ! function_exists( 'rocket_clean_post' ) ) {
return; // WP Rocket not active; skip silently
}
rocket_clean_post( $post_id );
OR, with the constant guard:
if ( defined( 'WP_ROCKET_VERSION' ) ) {
rocket_clean_post( $post_id );
}
Both work; the function_exists form is more defensive (handles the rare case where WP Rocket is partially loaded). Pick one and stay consistent.
For namespaced PHP code, use a full backslash: if ( ! \function_exists( 'rocket_clean_post' ) ) return;.
2. Pick the right granularity
| Scenario | Right call |
|---|---|
| One specific post changed (CPT save, comment) | rocket_clean_post( $post_id ) |
| One specific URL changed (custom permalink, programmatic page) | rocket_clean_files( [ $url ] ) |
| Taxonomy archive needs refresh (term added / renamed) | rocket_clean_term( $term_id, $taxonomy ) |
| User profile changed | rocket_clean_user( $user_id ) |
| Homepage needs refresh (e.g. featured post change) | rocket_clean_home() |
| Site-wide change (theme switch, options change) | rocket_clean_domain() |
| Asset pipeline change (new minify rule, dev → prod) | rocket_clean_minify() + rocket_clean_cache_busting() |
Do not call rocket_clean_domain() from frequent events. It's the heaviest operation; over-using it defeats WP Rocket's whole purpose by constantly re-warming. Reserve it for actually-site-wide changes.
3. Hook into post-save events
// Most common pattern — clear when a CPT entry is saved.
add_action( 'save_post_my_cpt', function ( int $post_id, \WP_Post $post, bool $update ): void {
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return;
}
if ( ! \function_exists( 'rocket_clean_post' ) ) {
return;
}
rocket_clean_post( $post_id );
}, 10, 3 );
save_post_<cpt> is preferable to plain save_post (more specific). Skip revisions and autosaves — both fire save_post but neither matters for cache.
4. Hook into WooCommerce events
// Order completion → clear product page cache
add_action( 'woocommerce_order_status_completed', function ( int $order_id ): void {
if ( ! \function_exists( 'rocket_clean_post' ) ) {
return;
}
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
foreach ( $order->get_items() as $item ) {
$product_id = $item->get_product_id();
if ( $product_id ) {
rocket_clean_post( $product_id ); // each ordered product's page
}
}
}, 20, 1 );
Reason: stock count changes after a sale; cached product pages show stale stock. WP Rocket has its own WC integration but third-party plugins that touch stock or display cached prices need their own invalidation.
5. Bulk imports — defer invalidation
function my_import_posts( array $records ): void {
// Suppress WP Rocket's automatic purges during the import.
add_filter( 'rocket_is_importing', '__return_true' );
foreach ( $records as $record ) {
wp_insert_post( $record );
}
// Restore + nuke once.
remove_filter( 'rocket_is_importing', '__return_true' );
if ( \function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
}
For a 10K-post import, calling rocket_clean_post() per-row generates 10K cache wipes + 10K filesystem operations. One rocket_clean_domain() at the end is orders of magnitude faster.
6. Lifecycle action hooks for monitoring
Verified in inc/functions/files.php and inc/common/purge.php — the public clean functions fire before_* and after_* actions:
| Function | Actions fired |
|---|---|
rocket_clean_post |
before_rocket_clean_post, after_rocket_clean_post |
rocket_clean_files |
before_rocket_clean_files, after_rocket_clean_files (per URL: before_rocket_clean_file, after_rocket_clean_file) |
rocket_clean_term |
before_rocket_clean_term, after_rocket_clean_term |
rocket_clean_user |
before_rocket_clean_user, after_rocket_clean_user |
rocket_clean_home |
before_rocket_clean_home, after_rocket_clean_home |
rocket_clean_home_feeds |
before_rocket_clean_home_feeds, after_rocket_clean_home_feeds |
rocket_clean_domain |
per URL: before_rocket_clean_domain, after_rocket_clean_domain; after the full run: rocket_after_clean_domain |
rocket_clean_minify |
before_rocket_clean_minify, after_rocket_clean_minify |
rocket_clean_cache_busting |
before_rocket_clean_busting, after_rocket_clean_cache_busting |
rocket_clean_cache_dir |
before_rocket_clean_cache_dir, after_rocket_clean_cache_dir |
Use these for:
- CDN purges triggered by cache wipes (
after_rocket_clean_post→ call your CDN API). - Audit logging — record cache-invalidation events with the post / URL info.
- Custom invalidation chains — when post X changes, also invalidate URL Y.
// Audit log — every invalidation
add_action( 'after_rocket_clean_domain', function ( string $root, string $lang, string $url ): void {
do_action( 'qm/info', "WP Rocket: domain cache cleared for {$url} (lang: {$lang})" );
}, 10, 3 );
// Trigger Cloudflare purge after WP Rocket clears a post
add_action( 'after_rocket_clean_post', function ( $post, array $purge_urls ): void {
foreach ( $purge_urls as $url ) {
my_cloudflare_purge( $url );
}
}, 10, 2 );
7. Multisite considerations
rocket_clean_domain( $lang ) clears the cache for the current site's domain. On multisite, switching context first:
foreach ( get_sites() as $site ) {
switch_to_blog( $site->blog_id );
if ( \function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
restore_current_blog();
}
rocket_clean_cache_dir() clears the entire cache directory on disk — across all blogs at once. Use for "I'm activating WP Rocket-affecting changes globally" (theme switch, network-wide settings rollout).
8. The $run_actions flag in rocket_clean_files
Verified at inc/functions/files.php:547 — rocket_clean_files( $urls, $filesystem = null, $run_actions = true ). The third arg defaults to true — fires the before_/after_ hooks per file. Pass false for silent purge if you're calling it from inside another after_rocket_clean_* hook (avoids infinite recursion).
add_action( 'after_rocket_clean_post', function ( $post, array $purge_urls ) {
// Don't fire actions on these supplementary cleans (we're already inside an action)
rocket_clean_files( [ home_url( '/related-feed' ) ], null, $run_actions = false );
}, 10, 2 );
Critical rules
- Always feature-detect with
function_exists('rocket_clean_post')ordefined('WP_ROCKET_VERSION'). WP Rocket is paid; not every site has it. wp_cache_flush()does NOT clear WP Rocket cache. Two different layers — object cache vs file cache.is_plugin_active('wp-rocket/wp-rocket.php')requireswp-admin/includes/plugin.php— not available during early hooks. Usedefined/function_existsinstead.- Pick the smallest granularity that covers the change.
rocket_clean_postoverrocket_clean_domainfor a single-post change. - Skip revisions / autosaves when hooking
save_post— they fire but don't change visible content. - Defer invalidation during bulk imports — temporarily return
truefromrocket_is_importing, insert posts, remove the filter, then callrocket_clean_domainonce. - Don't call
rocket_clean_domainfrom high-frequency events. Defeats WP Rocket's purpose. - Use
$run_actions = falseinrocket_clean_fileswhen calling from inside another action handler to avoid recursion. - Multisite:
switch_to_blog+restore_current_blogaround the cache call when iterating sites. - Never raw-
unlink()the cache files. Filename layout is non-trivial (desktop / mobile / lang / query-string variants); manual deletion leaves stale variants. rocket_clean_minifyandrocket_clean_cache_bustingare for CSS / JS asset pipeline changes, not for HTML cache.
Common mistakes
// WRONG — wp_cache_flush expecting WP Rocket to clear
function my_save_handler( $post_id ): void {
update_post_meta( $post_id, 'foo', 'bar' );
wp_cache_flush(); // 🔴 only clears object cache, not WP Rocket file cache
}
// RIGHT
function my_save_handler( $post_id ): void {
update_post_meta( $post_id, 'foo', 'bar' );
if ( \function_exists( 'rocket_clean_post' ) ) {
rocket_clean_post( $post_id );
}
}
// WRONG — raw filesystem deletion
function my_clear() {
$cache_dir = WP_CONTENT_DIR . '/cache/wp-rocket/example.com';
array_map( 'unlink', glob( $cache_dir . '/*.html' ) ); // 🔴 misses mobile / lang / qs variants
}
// RIGHT
if ( \function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
// WRONG — is_plugin_active during plugins_loaded
add_action( 'plugins_loaded', function () {
if ( is_plugin_active( 'wp-rocket/wp-rocket.php' ) ) { // 🔴 fatal — function not loaded
// ...
}
} );
// RIGHT
add_action( 'plugins_loaded', function () {
if ( ! defined( 'WP_ROCKET_VERSION' ) ) return;
// ...
}, 11 );
// WRONG — clean_domain on every post save
add_action( 'save_post', function ( $post_id ) {
if ( \function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain(); // 🔴 nukes the entire site cache for one post change
}
} );
// RIGHT — granular
add_action( 'save_post', function ( $post_id ) {
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) return;
if ( \function_exists( 'rocket_clean_post' ) ) {
rocket_clean_post( $post_id );
}
} );
// WRONG — no autosave / revision skip
add_action( 'save_post', function ( $post_id ) {
rocket_clean_post( $post_id ); // fires on every keystroke during autosave
} );
// RIGHT
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) return;
rocket_clean_post( $post_id );
// WRONG — clean inside a clean (recursion)
add_action( 'after_rocket_clean_post', function ( $post ) {
rocket_clean_files( [ get_permalink( $post->ID ) ] ); // 🔴 fires after_rocket_clean_files which can re-trigger
} );
// RIGHT — pass $run_actions = false
add_action( 'after_rocket_clean_post', function ( $post ) {
rocket_clean_files( [ get_permalink( $post->ID ) ], null, false );
} );
// WRONG — bulk import without deferral
foreach ( $records as $record ) {
wp_insert_post( $record );
rocket_clean_post( /* ... */ ); // 🔴 N cache wipes for N records
}
// RIGHT — defer to one nuke
add_filter( 'rocket_is_importing', '__return_true' );
foreach ( $records as $record ) {
wp_insert_post( $record );
}
remove_filter( 'rocket_is_importing', '__return_true' );
if ( \function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
// WRONG — assume one implicit home clean covers every language variant
rocket_clean_home(); // ambiguous on multilingual sites; depends on the active i18n integration/context
// RIGHT — iterate the active language codes from WPML / Polylang / TranslatePress
foreach ( [ 'en', 'de', 'fr' ] as $lang ) {
rocket_clean_home( $lang );
}
Cross-references
- Run
wp-rocket-cache-rejection-and-filterswhen the answer isn't "clear cache after change" but "PREVENT this URL / path from being cached at all". - Run
wp-plugin-cronwhen invalidation is scheduled / batched (Action Scheduler, WP-Cron) — combine withrocket_clean_*calls in the cron handler. - Run
wcs-renewal-schedulerif cache-invalidation triggers come from WC Subscription renewal events. - Run
wp-plugin-options-storagewhen deciding "should I cache this manually OR let WP Rocket handle it" — most often: let WP Rocket do it.
What this skill does NOT cover
- WP Rocket settings UI / admin pages. Out of scope; integrators don't touch UI.
- Internal
Engine/classes. Private; signatures change between versions. Use the public functions. - The WP Rocket REST API (admin-side, paid). Not designed for third-party invalidation.
.htaccessrewrite rules WP Rocket installs. Server-config concern; integrators don't modify these.- Cloudflare / Varnish / CDN-specific addons. Each has its own surface; this skill is core WP Rocket only.
rocket_bufferfilter for HTML output manipulation. Niche + dangerous; covered inwp-rocket-cache-rejection-and-filterswith strong warnings.- License / activation key handling. Premium-specific; not integrator-facing.
References
- Plugin entry: wp-content/plugins/wp-rocket/wp-rocket.php — version constants, header.
rocket_clean_post: inc/common/purge.php:167 — handles auto-draft / draft / nav_menu_item / attachment skips, firesbefore_/after_rocket_clean_post.rocket_clean_files: inc/functions/files.php:547 —$run_actionsthird arg controls whetherbefore_/after_rocket_clean_filesand per-URLbefore_/after_rocket_clean_filefire.rocket_clean_home: inc/functions/files.php:679.rocket_clean_domain: inc/functions/files.php:821.rocket_clean_term: inc/functions/files.php:924.rocket_clean_user: inc/functions/files.php:996.rocket_clean_cache_dir: inc/functions/files.php:1060.rocket_clean_minify: inc/functions/files.php:354.- WP Rocket plugin compatibility doc: https://docs.wp-rocket.me/article/92-plugin-compatibility-with-wp-rocket.