← Back to blog
· by Nick Bryant · in Blog

Cleared every cache and still seeing stale pages? Your old caching plugin may still be running

Terminal showing an orphaned advanced-cache.php drop-in left behind by a deleted caching plugin

You’ve made the change. It shows in the editor, it shows in the preview, and the client still can’t see it.

So you start clearing caches. The page cache. The CDN. Cloudflare, twice, with dev mode on for good measure. A hard refresh, an incognito window, a different browser on a different machine. Nothing. The front end keeps serving a version of the page that’s days or weeks old.

Then someone adds a random query string to the URL, something like /about/?nocache=1, and the fresh page appears instantly.

If this sounds familiar, here’s the question worth asking before you blame your host: did this site ever run a caching plugin that’s since been removed?

We’ve diagnosed this exact situation several times in support recently. One agency migrating around 100 client sites away from third-party caching plugins found stale pages on at least three of them, weeks after the plugins were gone. Another case involved a staging site that kept showing the old production design: the production site’s caching plugin had been cloned across with the site, cache and all, and even though it looked switched off, it was still quietly serving old copies of a handful of URLs.

In every case the site owner had checked the obvious places and found nothing. As one of them put it: what is telling these pages to serve from a cache folder if there’s no plugin, there’s nothing in .htaccess, and every cache has been cleared multiple times?

The answer lives in how WordPress page caching actually works, and in what caching plugins leave behind when they go.

Why deleting a caching plugin isn’t always the end of it

The drop-in that outlives its plugin

Page caching plugins don’t do their main job as normal plugins. Normal plugins load after most of WordPress core, which is far too late to serve a cached page quickly. So caching plugins install a drop-in: a file named advanced-cache.php placed directly in wp-content/. They also add one line to wp-config.php:

define( 'WP_CACHE', true );

With that constant set, WordPress loads wp-content/advanced-cache.php on every single request, before any plugin, before the theme, before almost everything. If a cached copy of the requested URL exists on disk, the drop-in serves it and exits. The rest of WordPress never runs. That’s what makes page caching fast, and it’s also what makes this problem so hard to spot:

  • Drop-ins aren’t plugins. They don’t appear in your list of installed plugins, they have no activation state, and they have no uninstall routine of their own.
  • The only places WordPress admits they exist are Tools > Site Health > Info > Drop-ins, and a “Drop-in” filter on the Plugins screen that only shows up if you know to look for it.
  • Crucially, an orphaned advanced-cache.php doesn’t necessarily need its parent plugin to keep serving cache files that already exist on disk.

Cleanup code only runs if WordPress runs it

A well-behaved caching plugin removes its drop-in and reverses its wp-config.php and .htaccess changes when you deactivate it, and removes its settings when you delete it. But that cleanup is just code, and code only runs when WordPress runs it:

  • Deactivate the plugin in wp-admin or with WP-CLI, and its deactivation routine runs.
  • Delete it from the Plugins screen, and its uninstall routine runs.
  • Delete the plugin folder over SFTP, and nothing runs at all.

That last one is exactly what happens when a caching plugin breaks a site and you remove it the fast way. It’s also effectively what happens when a site is cloned or migrated: the copy includes the drop-in, the config files, and the entire cache directory, but no uninstall routine ever fires on the new copy. The plugin folder may be gone, so the plugin list looks clean, but advanced-cache.php, the WP_CACHE constant, the .htaccess rules, and megabytes of pre-generated HTML are all still in place, still doing their job. Serving the site as it looked on the day the cache was last written.

Sometimes PHP isn’t even involved

Some plugins go a step further for speed and write rewrite rules into .htaccess so that Apache serves the cached HTML file directly from disk. WP Super Cache in mod_rewrite mode, WP Fastest Cache, and W3 Total Cache’s disk-enhanced mode all work this way. With those rules in place, a request for a cached page never touches PHP at all.

Which means you can delete the plugin, delete the drop-in, and remove WP_CACHE from wp-config.php, and the server will keep serving stale HTML for as long as both the rules and the cached files remain.

LiteSpeed Cache is a further variation: its cache store lives at the web server level, outside your WordPress install entirely. Deleting the plugin doesn’t purge it, and the leftover # BEGIN LSCACHE block in .htaccess keeps telling the server to serve from it.

Even clean uninstalls leave things behind

This isn’t only about worst-case removals. Some plugins skip parts of cleanup deliberately. FlyingPress documents that deleting the plugin leaves wp-content/cache/flying-press/ in place, to avoid timeouts when deleting large cache trees. WP Super Cache’s own documentation notes that uninstalling doesn’t remove its mod_rewrite rules. W3 Total Cache states plainly that deactivation and deletion won’t remove the files and changes it has added.

None of this is malicious. It’s the predictable result of how much of your install a page caching plugin has to touch to do its job. But it does mean that “I deleted the plugin” and “the plugin is gone” are two different statements.

Diagnose it in five minutes

Work through these checks in order. You’ll usually have your answer by the second one.

1. Read the page source

Load the stale page while logged out and view the source. Most caching plugins sign their work with an HTML comment near the bottom of the page:

  • WP Rocket: <!-- This website is like a Rocket ... Performance optimized by WP Rocket -->
  • WP Super Cache: <!-- Cached page generated by WP-Super-Cache on 2026-07-13 10:15:32 -->
  • WP Fastest Cache: <!-- WP Fastest Cache file was created in 0.6 seconds -->
  • W3 Total Cache: <!-- Performance optimized by W3 Total Cache -->

A signature from a plugin you’ve already removed is your smoking gun. So is a generation timestamp that doesn’t change when you reload.

One subtlety we’ve verified in testing: the signed page can outlive the plugin in other caches too. A CDN or your host’s server-level cache may have stored a copy of the plugin-generated HTML, comment and all, and will keep serving it after the plugin is gone until those layers are purged. The signature tells you which plugin generated the page, not necessarily which layer is serving it right now.

2. Check the response headers

curl -I https://example.com/affected-page/

Typical output, trimmed to the interesting lines:

HTTP/2 200
server: nginx
last-modified: Sat, 27 Jun 2026 20:33:37 GMT
x-cache-status: HIT
cache-control: max-age=300, must-revalidate

Three things to look for:

  • Plugin-specific headers. FlyingPress adds x-flying-press-cache: HIT, LiteSpeed adds x-litespeed-cache: hit. A caching header from a plugin that isn’t installed answers the question on the spot.
  • A suspiciously old last-modified. In one case we worked on, the last-modified header matched the timestamp of a gzipped file in wp-content/cache/flying-press/ to the second. The “web server” answering requests was a folder of old HTML.
  • Leftover cache-control. Seeing Cache-Control: max-age=0, must-revalidate on every page is a common cache-plugin fingerprint. As a bonus problem, headers like this can stop your host’s server-level cache from working at all, so the site gets slower and stays stale.

3. Look for drop-ins

List wp-content/ over SSH or SFTP and look for files that shouldn’t be there. Here’s a real listing from a test site where we deleted WP Super Cache’s folder while it was active. The plugin is gone from plugins/, but look what stayed:

$ ls -la wp-content/
-rw-r--r--  advanced-cache.php     # drop-in, still loading on every request
drwxr-xr-x  cache                  # full page cache, still on disk
-rw-r--r--  index.php
drwxr-xr-x  plugins                # wp-super-cache no longer here
drwxr-xr-x  themes
drwxrwxrwx  uploads
-rw-r--r--  wp-cache-config.php    # orphaned config

The files to know: advanced-cache.php (page cache drop-in), object-cache.php (object cache drop-in), db.php (database drop-in, W3 Total Cache), wp-cache-config.php (WP Super Cache config), the cache/ directory, and anything unexpected in mu-plugins/.

Or with WP-CLI, where a plain wp plugin list includes drop-ins at the bottom:

$ wp plugin list
name                status    update  version
akismet             inactive  none    5.7
hello               inactive  none    1.7.2
hosting-tools       active    none    3.3
advanced-cache.php  dropin    none

A drop-in with no caching plugin above it in the list is exactly the situation we’re describing. You can also filter directly with wp plugin list --status=dropin and wp plugin list --status=must-use.

WP-CLI terminal: wp plugin delete wp-super-cache succeeds, and the next plugin list still shows advanced-cache.php as a drop-in
The reproduction in a WP-CLI terminal: the plugin is deleted while active, and the next plugin list still shows the orphaned drop-in.

You can also check Tools > Site Health > Info > Drop-ins in wp-admin, or the easily missed Drop-in filter on the Plugins screen itself.

WordPress Site Health Info screen with the Drop-ins section expanded, listing advanced-cache.php
Site Health > Info > Drop-ins lists the file WordPress is still loading on every request.

There’s one more tell on the Plugins screen. If the plugin folder was deleted while the plugin was active, WordPress shows a notice like “The plugin wp-super-cache/wp-cache.php has been deactivated due to an error: Plugin file does not exist.” That message means WordPress dropped the vanished plugin from its active list, but no cleanup code ever ran.

WordPress Plugins screen showing the missing plugin file error and the Drop-in filter holding advanced-cache.php
The Plugins screen after the folder vanished: the missing-file notice, and the Drop-in filter still holding advanced-cache.php.

Open any advanced-cache.php you find. The header comment names the plugin that created it.

One caution: wp cache flush won’t help you here. It flushes the object cache, not page cache files on disk.

4. Check wp-config.php and .htaccess

grep -n "WP_CACHE\|WPCACHEHOME" wp-config.php
grep -n "# BEGIN" .htaccess

From the same test site, after the plugin was long gone:

$ grep -n "WP_CACHE\|WPCACHEHOME" wp-config.php
23:define('WP_CACHE', true);
24:define( 'WPCACHEHOME', '.../wp-content/plugins/wp-super-cache/' );

That second constant points at a directory that no longer exists. WordPress doesn’t mind: it keeps loading the drop-in on every request regardless.

Marker blocks that caching plugins leave in .htaccess:

Marker Plugin
# BEGIN WPSuperCache WP Super Cache
# BEGIN WP ROCKET WP Rocket
# BEGIN W3TC ... (several blocks) W3 Total Cache
# BEGIN LSCACHE / # BEGIN NON_LSCACHE LiteSpeed Cache
# BEGIN WpFastestCache WP Fastest Cache

5. The query string trick

Rewrite-based caches typically skip requests with query strings. If https://example.com/page/?x=1 shows fresh content while https://example.com/page/ shows stale content, the server is serving static files from disk, and no amount of purging from wp-admin will fix it. You need to remove the files and the rules.

Plugin-by-plugin cleanup

Take a full backup before editing any of these files. All paths are relative to the WordPress root. If another caching plugin is still active on the site, leave WP_CACHE and that plugin’s own files alone.

FlyingPress

What can be left behind:

  • wp-content/advanced-cache.php (the header comment identifies FlyingPress)
  • wp-content/cache/flying-press/, which remains even after a clean uninstall, by design
  • define( 'WP_CACHE', true ); in wp-config.php

Cleanup:

  1. Delete wp-content/advanced-cache.php if its header names FlyingPress.
  2. Delete wp-content/cache/flying-press/.
  3. Remove the WP_CACHE line from wp-config.php if no other caching plugin needs it.
  4. Reload the affected page and confirm the x-flying-press-cache header is gone.

FlyingPress covers step 2 in its uninstall documentation.

WP Rocket

WP Rocket cleans up properly when you deactivate and delete it through wp-admin. The trouble starts when the folder is deleted over SFTP or the site is cloned, because the cleanup that normally runs on deactivation never fires. What can be left behind:

  • wp-content/advanced-cache.php
  • # BEGIN WP ROCKET ... # END WP ROCKET in .htaccess
  • define( 'WP_CACHE', true ); in wp-config.php
  • wp-content/wp-rocket-config/
  • wp-content/cache/wp-rocket/, plus cache/min/, cache/busting/, cache/used-css/, cache/critical-css/, and cache/fonts/
  • In the database: the wp_rocket_settings option, wp_wpr_* tables, and rocket_* cron events

Cleanup:

  1. Delete wp-content/advanced-cache.php.
  2. Remove everything between # BEGIN WP ROCKET and # END WP ROCKET in .htaccess.
  3. Delete wp-content/wp-rocket-config/ and the cache folders listed above.
  4. Remove the WP_CACHE line from wp-config.php if nothing else needs it.
  5. Optionally clear the database leftovers: delete rocket_* cron events (WP Crontrol makes this easy), the wp_rocket_settings option, and the wp_wpr_* tables.

WP Rocket publishes the full list in its uninstall documentation.

WP Super Cache

The classic case, and its own documentation is refreshingly honest about it: uninstalling doesn’t remove the mod_rewrite rules. We reproduced the bad-removal path on a test site: deleting the plugin folder while it was active left the drop-in, the config file, the WP_CACHE constant, and the full cache directory in place. To its credit, WP Super Cache’s orphaned drop-in fails safe rather than serving stale pages on its own. The bigger risks are its mod_rewrite rules on Apache, which serve cached files with no PHP involved, and the leftover files blocking whatever caching you set up next. What can be left behind:

  • wp-content/advanced-cache.php and wp-content/wp-cache-config.php
  • WP_CACHE and WPCACHEHOME constants in wp-config.php
  • # BEGIN WPSuperCache ... # END WPSuperCache rules in .htaccess (mod_rewrite mode on Apache), which serve cached HTML with PHP never running
  • wp-content/cache/supercache/<your-hostname>/, one folder of static HTML per page:
$ ls -la wp-content/cache/supercache/example.com/
-rw-r--r--  69740  index-https.html    # your homepage, frozen in time

Cleanup:

  1. Delete wp-content/advanced-cache.php and wp-content/wp-cache-config.php.
  2. Remove the WP_CACHE and WPCACHEHOME lines from wp-config.php.
  3. Remove everything between # BEGIN WPSuperCache and # END WPSuperCache in .htaccess.
  4. Delete wp-content/cache/.
  5. Re-save your permalinks (Settings > Permalinks) to rebuild .htaccess cleanly.

These steps come from the plugin’s own emergency uninstall guide.

W3 Total Cache

The most thorough offender: it can install three separate drop-ins, and deactivating or deleting it removes essentially nothing. What can be left behind:

  • Three drop-ins: wp-content/advanced-cache.php, wp-content/object-cache.php, and wp-content/db.php
  • wp-content/w3tc-config/
  • wp-content/cache/
  • Several # BEGIN W3TC ... blocks in .htaccess (page cache, browser cache, minify)
  • WP_CACHE in wp-config.php

An orphaned W3TC drop-in is also a classic cause of the “some files appear to be missing or out of place” error and of white screens after removal, because object-cache.php and db.php try to load plugin files that no longer exist.

Cleanup:

  1. If the plugin is still installed, disable every cache type in its settings first, then deactivate and delete it.
  2. Delete advanced-cache.php, object-cache.php, and db.php from wp-content/ (check each file’s header to confirm W3TC owns it).
  3. Delete wp-content/w3tc-config/ and wp-content/cache/.
  4. Remove every # BEGIN W3TC block from .htaccess.
  5. Remove the WP_CACHE line from wp-config.php.
  6. Re-save permalinks.

LiteSpeed Cache

Different beast: the cache store belongs to the web server, not WordPress. Deleting the plugin doesn’t purge it. What can be left behind:

  • # BEGIN LSCACHE and # BEGIN NON_LSCACHE blocks in .htaccess, which keep server-level caching switched on
  • Browser cache rules (ExpiresByType text/html) that pin stale copies in your visitors’ browsers
  • The server-level cache store itself, which lives outside your install
  • Sometimes wp-content/object-cache.php if Redis or Memcached was configured

Cleanup:

  1. Remove the LSCACHE and NON_LSCACHE blocks and any leftover Expires rules from .htaccess.
  2. Purge the server-level cache. On shared LiteSpeed hosting only your host can do this, so ask.
  3. Delete wp-content/object-cache.php if LiteSpeed created it.
  4. Because of the browser-cache rules, existing visitors may hold stale copies until their local cache expires. Test from a fresh profile.

WP Fastest Cache

What can be left behind:

  • # BEGIN WpFastestCache ... # END WpFastestCache rules in .htaccess, serving cached files directly
  • wp-content/cache/all/ and wp-content/cache/wpfc-mobile-cache/
  • wpFastestCache* options and a cleanup cron event

Cleanup:

  1. Remove the WpFastestCache block from .htaccess.
  2. Delete wp-content/cache/all/, wp-content/cache/wpfc-mobile-cache/, and wp-content/cache/tmpWpfc/ if present.
  3. Delete wpFastestCache options and cron events if you want a fully clean database.

Also worth checking

  • Hummingbird leaves advanced-cache.php and wp-content/wphb-cache/ behind on deactivation.
  • Swift Performance installs wp-content/mu-plugins/swift-performance-loader.php, which loads even when the plugin is deactivated.
  • Comet Cache documents manual removal of advanced-cache.php, the WP_CACHE line, and wp-content/comet-cache/.
  • WP-Optimize points its advanced-cache.php at wp-content/cache/wpo-cache/.
  • Endurance Page Cache arrives as a host-injected mu-plugin on some shared hosts, can’t be deactivated from wp-admin, and leaves .htaccess directing traffic to cached pages after it’s disabled.
  • Redis Object Cache and similar object cache plugins leave wp-content/object-cache.php, which usually produces a 500 error rather than stale pages once the plugin is gone.
  • Cache Enabler deserves a mention as the counter-example: it removes its drop-in and unsets WP_CACHE on deactivation. You may still want to delete wp-content/cache/cache-enabler/.

Quick reference

Plugin Files to check .htaccess marker
FlyingPress advanced-cache.php, cache/flying-press/ none
WP Rocket advanced-cache.php, wp-rocket-config/, cache/wp-rocket/ # BEGIN WP ROCKET
WP Super Cache advanced-cache.php, wp-cache-config.php, cache/supercache/ # BEGIN WPSuperCache
W3 Total Cache advanced-cache.php, object-cache.php, db.php, w3tc-config/ # BEGIN W3TC ...
LiteSpeed Cache server-level store, sometimes object-cache.php # BEGIN LSCACHE
WP Fastest Cache cache/all/, cache/wpfc-mobile-cache/ # BEGIN WpFastestCache

All file paths are inside wp-content/, and WP_CACHE in wp-config.php applies to all of them except LiteSpeed’s server-level store.

How to remove a caching plugin cleanly

Next time, in this order:

  1. Turn the plugin’s features off first. Especially with W3 Total Cache: disable every cache type in its settings and save.
  2. Deactivate in wp-admin, not by renaming or deleting folders. Deactivation is where most caching plugins remove their drop-in and reverse their wp-config.php and .htaccess changes.
  3. Delete from the Plugins screen, which runs the uninstall routine.
  4. Verify. Check wp-content/ for drop-ins, wp-config.php for WP_CACHE, .htaccess for marker blocks, and wp-content/cache/ for leftovers. Two minutes, and you’ll never be haunted later.
  5. Re-save permalinks if you edited .htaccess.
  6. Purge your CDN. Edge caches may have picked up stale copies while the leftover cache was serving them, and they’ll keep those copies until told otherwise.

And the special case that catches even careful teams: clones carry the cache with them. If you’re about to clone a site to staging, or push a staging site live, remove the caching plugin properly on the source site first. Otherwise the copy arrives with a drop-in and a folder of pre-generated pages, and no plugin in the list to explain them.

The two-minute takeaway

Stale pages that survive every cache purge are almost never a mystery once you know where to look: read the page source, read the response headers, and check wp-content/ for drop-ins that outlived their plugin.

It’s also worth knowing whether your host caches at the server level. Wordify’s SmartCache works that way, so there’s no advanced-cache.php woven into your install, and removing it can’t leave a ghost behind. On any managed host, if a page won’t update no matter what you purge, leftover files from a previously removed caching plugin should be your first suspect, and your host’s support team can check the file system for you in minutes. Ours is around 24/7 if you need us.