You edit a page, change some CSS or update functions.php, hit refresh, and nothing changes. Maybe it shows for you when logged in but not for visitors, or it shows on desktop but not on your phone. When WordPress changes are not showing, the change almost always worked; a cache somewhere is still serving the old version. This guide walks through each caching layer in the order a request hits it, shows how to identify which one is answering, and how to clear it safely.
Quick fix
- Open the page in a private window. If the change appears, it was your browser cache.
- Purge your page cache plugin (LiteSpeed Cache, WP Rocket, W3 Total Cache and so on) from its admin bar menu.
- Purge the CDN. In Cloudflare: Caching, Configuration, Purge Everything (or purge just the URL).
- If CSS or JS changes still don’t load, the file URL hasn’t changed. Version your assets with
filemtime()as shown below. - If PHP changes don’t take effect at all, restart PHP or reset OPcache from your hosting panel.
The caching layers, from browser to database
A request for a WordPress page can be answered by any of these, and each one stops the layers below it from ever seeing the request:
- Browser cache: stores pages and assets according to
Cache-Controlheaders. - CDN or edge cache: Cloudflare, a host’s built-in CDN, Varnish or Nginx FastCGI cache.
- Server page cache: static HTML files served by web server rules before PHP runs.
- PHP OPcache: compiled PHP held in memory.
- Persistent object cache: Redis or Memcached storing options, queries and transients.
1. Browser cache
The most common cause for CSS and JavaScript. If your theme enqueues style.css with a fixed version, the URL never changes and the browser keeps its copy. A hard refresh (Ctrl+Shift+R, or Cmd+Shift+R on Mac) or DevTools with “Disable cache” ticked rules it out for you, but your visitors still have the old file. The fix is to change the URL whenever the file changes:
add_action( 'wp_enqueue_scripts', function () {
$rel = 'assets/css/main.css';
wp_enqueue_style(
'theme-main',
get_theme_file_uri( $rel ),
[],
filemtime( get_theme_file_path( $rel ) )
);
} );
WordPress appends ?ver= with the file’s modification time, so every deploy produces a new URL and every cache below treats it as a new file.
2. CDN or edge cache
Cloudflare only caches static files by default, but a “Cache Everything” rule or APO caches HTML too. Your host may also run its own edge or Varnish cache with a separate purge button. If the page is cached at the edge, purging WordPress plugins changes nothing for visitors until the edge copy expires or is purged.
3. Server page cache served before PHP
Page cache plugins write finished HTML to disk and then serve it without loading WordPress at all. WP Rocket and W3 Total Cache can add rewrite rules to .htaccess that hand Apache the cached file directly. LiteSpeed Cache uses rules that the LiteSpeed server applies itself:
# BEGIN LSCACHE
<IfModule LiteSpeed>
RewriteEngine on
CacheLookup on
</IfModule>
# END LSCACHE
Because PHP never runs, nothing you change in WordPress affects these pages until the cache is purged. If the plugin was removed by deleting its folder instead of deactivating it, its rules stay behind in .htaccess and can keep serving stale files from wp-content/cache/.
4. OPcache
OPcache keeps compiled PHP in memory. On servers where opcache.validate_timestamps is 0 (common on tuned production stacks), PHP never checks whether files changed, so your edited functions.php or template is ignored until OPcache is reset. Restart PHP-FPM or use your host’s “flush OPcache” option. Running opcache_reset() from the command line does not help, because the CLI has its own separate OPcache.
5. Object cache and orphaned drop-ins
This is the layer experienced developers miss. WordPress loads special “drop-in” files from wp-content/ regardless of which plugins are active:
object-cache.php: replaces the object cache, usually with Redis or Memcached. Loaded on every request if the file exists.advanced-cache.php: runs before most of WordPress to serve page cache, whenWP_CACHEistrueinwp-config.php.db.php: replaces the database class, used by some query caching and monitoring tools.
Deactivating a cache plugin does not always remove these files. An orphaned object-cache.php can keep returning stale options, menus or widget settings from Redis, so changes saved in the admin seem to vanish. List the drop-ins and flush the object cache:
wp plugin list --status=dropin
wp cache flush
wp transient delete --all
You can also see them in wp-admin under Plugins, Drop-ins.
Step-by-step: find which layer is serving the page
Inspect the response headers with a cache-busting query string so you get a fresh view:
curl -sI "https://example.com/about/"
| grep -iE '^(cf-cache-status|age|cache-control|x-litespeed-cache|x-cache|x-proxy-cache|server):'
curl -sI "https://example.com/about/?nocache=$(date +%s)"
cf-cache-status: HITmeans Cloudflare served it from the edge.DYNAMICorBYPASSmeans Cloudflare passed it to your server.- An
ageheader shows how many seconds the copy has been cached. x-litespeed-cache: hitmeans LiteSpeed served its page cache.x-cacheorx-proxy-cacheheaders usually come from Varnish, Nginx or a host-level cache.
Then view the page source and scroll to the bottom. Many page cache plugins append an HTML comment naming themselves and when the page was cached. If that timestamp predates your change, you’ve found the layer.
Work through it in order:
- Private window shows the change? Browser cache. Fix asset versioning.
- Headers show an edge
HIT? Purge the CDN or host cache. - Cache comment in the HTML with an old time? Purge the page cache plugin.
- Logged-in view also stale, or saved settings revert? Check
object-cache.phpand flush the object cache. - PHP edits have no effect even with all caches bypassed? Reset OPcache, and confirm you edited the file on the right server and the right theme (a child theme may be overriding it).
Safely cleaning up orphaned drop-ins
- Back up
wp-content/andwp-config.php. - Open each drop-in and read the header comment to see which plugin created it.
- If that plugin is no longer installed, rename the file (for example
object-cache.php.bak) rather than deleting it, then test the site. - If you removed the page cache plugin, set
define( 'WP_CACHE', false );or remove the line, and delete its leftover block from.htaccess. - Delete the old cache folder under
wp-content/cache/once the site works without it.
Do not remove object-cache.php if your host installed it as part of its platform. Ask them first.
Prevention
- Use one page cache, not two plugins plus a host cache fighting each other.
- Version every theme asset with
filemtime()or a build hash. - Make purging part of your deploy: page cache, CDN and OPcache.
- Deactivate cache plugins from wp-admin so they can clean up, rather than deleting their folders.
- Exclude cart, checkout and account pages from page cache. My WordPress Core Web Vitals checklist covers setting up caching that stays fast without going stale.
Key takeaways
- If WordPress changes are not showing, a cache is almost always serving an old copy.
- Check layers in order: browser, CDN, page cache, OPcache, object cache.
- Response headers like
cf-cache-statusandx-litespeed-cachetell you which layer answered. object-cache.php,advanced-cache.phpanddb.phpkeep working after a plugin is deactivated.filemtime()versioning ends stale CSS and JS for good.
Frequently asked questions
Why are my WordPress changes not showing for visitors but showing for me?
Page caches usually bypass logged-in users, so you see the live version while visitors get a cached copy. Purge the page cache plugin and your CDN.
Why is my CSS change not showing in WordPress?
The stylesheet URL hasn’t changed, so browsers and CDNs keep the old file. Enqueue it with filemtime() as the version so the URL updates each time you edit it.
I deactivated my cache plugin. Why is the site still cached?
Drop-in files like advanced-cache.php and object-cache.php, the WP_CACHE constant, or leftover .htaccess rules can keep caching active. Check Plugins, Drop-ins and remove what the old plugin left behind.
How do I tell if Cloudflare is caching my WordPress page?
Check the cf-cache-status response header with DevTools or curl -I. HIT means Cloudflare served it from cache; DYNAMIC means it came from your server.
If you have purged everything and the old version still won’t go away, get in touch and I’ll trace which layer is holding on to it. If redirects are also misbehaving behind Cloudflare, see fixing too many redirects with Cloudflare.