You changed something on your WordPress site – a post, a product price, a page title – and the old version keeps showing up. Or a CSS tweak will not render. Or a plugin update should have fixed a bug but the bug is still there. The culprit is almost always a cache somewhere between you and the visitor holding on to the old version of the content.
The frustrating part is that WordPress sites have up to seven different caches, each of which can serve stale content independently. Clearing one does not clear the others. If you flush your page cache plugin but Cloudflare still has the old HTML, your visitors still see the old version. If you purge Cloudflare but your browser has the old copy, you still see the old version even while everyone else sees the fix.
This guide walks through every cache layer, how to confirm whether it is the one serving stale content, and how to clear each one correctly – in the right order, starting closest to the user and working back toward the origin.
Why are my changes not showing?#
Before touching any cache, rule out the quick things:
- You edited a draft, not the live post. Check the post status in the admin.
- You edited a different page than the one you are looking at. Easy to do with duplicate titles or custom post types.
- You are logged in and seeing admin-specific content. Log-out or open a private/incognito window and compare.
- You did not save. Gutenberg’s autosave can look like it saved when the explicit “Update” button was never clicked.
If all four are ruled out and your changes still do not show, it is a cache problem. The rest of this guide covers the seven layers in order of proximity to the user.
The seven cache layers#
A request from a visitor’s browser to your WordPress site can be answered from any of these caches, depending on configuration. Each one has different controls and clears differently.
| # | Layer | What it stores | Usually controlled by |
|---|---|---|---|
| 1 | Browser cache | HTML, CSS, JS, images | The visitor’s browser |
| 2 | DNS cache | Domain-to-IP mapping | OS, browser, resolver |
| 3 | CDN cache | Full page responses and static assets | Cloudflare, BunnyCDN, Fastly |
| 4 | Server page cache | Rendered HTML at the web server | nginx FastCGI, Varnish |
| 5 | WordPress page cache plugin | Rendered HTML inside WordPress | WP Rocket, W3 Total Cache, LiteSpeed, Hostney Cache |
| 6 | WordPress object cache | PHP variables and DB query results | Redis, Memcached, or database transients |
| 7 | PHP OPcache | Compiled PHP bytecode | php-fpm |
Work through them top-down. Layer 1 is closest to the visitor and responsible for most “I cleared everything but nothing changed” complaints. Layer 7 is at the origin and only matters if you edited PHP files and the new code is not running.
Layer 1: browser cache#
Your browser stores static assets (CSS, JS, images, and sometimes HTML) locally to avoid re-downloading them on every page load. If you changed a stylesheet, the browser can keep serving the old version for hours or days depending on the cache-control headers the server sent with the original response.
How to confirm
Open the page in a private or incognito window. If the changes show up there but not in your normal window, the browser cache is the culprit. Private windows start with no cache state.
Or open DevTools (F12), go to the Network tab, check “Disable cache,” and reload the page. If the reload shows your changes, the browser cache was holding the old version.
How to clear
Hard refresh is usually enough:
Ctrl+Shift+R
(Windows/Linux) or
Cmd+Shift+R
(Mac) forces the browser to re-download the page ignoring the cache.
If a hard refresh does not work (service workers, aggressive caching headers), clear the browser data for the specific site: Chrome > Settings > Privacy and security > Site settings > find the site > Clear data. Firefox and Safari have equivalent per-site options.
Important: tell visitors to hard-refresh too. Your hard refresh only clears your browser, not theirs. For a site-wide fix, the answer is to bust the cache by changing the file URL (versioned file names like
style.css?v=20260423
) or configuring shorter cache-control headers.
Layer 2: DNS cache#
Rare, but worth mentioning. DNS caches are not supposed to affect content you see – only which server you connect to. But in specific situations (Cloudflare “Always Online” serving from their cache when the origin is down, or a CDN with a DNS-level failover) a stale DNS entry can mean you keep reaching the wrong cached version of your site.
How to confirm
# Does the domain resolve to what you expect?
dig yoursite.com +short
# Check against a known-good public resolver
dig @8.8.8.8 yoursite.com +short
If both return the same IP, DNS is fine. If they differ, your local DNS resolver has an old record.
How to clear
How to flush DNS cache on Linux, Mac, and Windows covers the OS-specific commands. One command per OS:
- Windows:
ipconfig /flushdns - macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder - Linux (systemd-resolved):
sudo resolvectl flush-caches
Browser-level DNS cache (separate from the OS) clears with the same site data clear from Layer 1, or in Chrome specifically by visiting
chrome://net-internals/#dns
and clicking “Clear host cache.”
Layer 3: CDN cache#
If your site is behind Cloudflare, BunnyCDN, Fastly, KeyCDN, or any other CDN, the CDN is serving cached copies of your pages and assets from its global edge locations. CDN caches are usually the biggest source of “my change is not live yet” problems because CDN cache TTLs are often set to hours or days.
How to confirm
Look at the response headers:
curl -I https://yoursite.com/ | grep -iE "cf-cache-status|x-cache|via|age"
-
cf-cache-status: HITmeans Cloudflare served this from its cache -
x-cache: HITmeans another CDN (Fastly, Varnish, etc.) served from cache -
age: 3600means the response has been cached for 3600 seconds
If you see a HIT status on the page that is not updating, the CDN is the layer to purge.
How to clear
Each CDN has its own purge controls:
Cloudflare: Dashboard > Caching > Configuration > “Purge Everything” (nuclear option) or “Custom Purge” to target specific URLs. For a specific page, enter its full URL with
https://
and click purge.
BunnyCDN: Pull Zones > select your zone > “Purge Cache.” Can target specific URLs or purge everything.
Fastly: Dashboard > Service > Purge. Supports single-URL, surrogate-key, and “Purge All.”
Custom CDN: check their docs – every CDN has a purge API. Most WordPress caching plugins integrate directly with major CDNs so a WordPress-level purge cascades to the CDN automatically, but you should verify by checking the CDN dashboard for the purge timestamp after saving the WordPress change.
Rule of thumb: if your CDN cache TTL is over 1 hour and you are making content changes more than once a day, you need automatic purge integration – not manual purging every time.
Cloudflare “Development Mode”
Cloudflare specifically has a “Development Mode” toggle that disables caching for 3 hours. Useful when you are making many changes and do not want to purge after every one. Dashboard > Caching > Configuration > Development Mode. Turn it back off when you are done – leaving it on permanently defeats the point of using a CDN.
For a broader discussion of when a CDN makes sense for a WordPress site at all, see should I use a CDN for my website?.
Layer 4: server page cache (nginx / Varnish)#
If your host uses nginx FastCGI caching or Varnish in front of PHP, a cached HTML response for your pages lives at the web server level – and is served without ever running WordPress. This is the biggest single performance win for a WordPress site, and the biggest source of “WordPress is flushing its cache but the old version is still live” confusion.
How to confirm
Check the response headers for a server-cache hit signal:
curl -I https://yoursite.com/ | grep -iE "x-cache|x-fastcgi-cache|x-proxy-cache|x-srcache"
The exact header name varies – nginx FastCGI cache typically uses
X-Cache-Status: HIT
, Varnish uses
X-Cache: HIT
or
Age: <seconds>
. If you see a HIT, the nginx/Varnish layer is the one serving the response.
How to clear
If your host manages this automatically (Hostney, Kinsta, WP Engine, etc.), server-level cache purge is triggered by the control panel or a dedicated plugin. No manual intervention needed on save – content changes flush automatically.
If you run your own server, purging depends on the specific configuration:
- nginx FastCGI cache: the cache files live at whatever path you configured
fastcgi_cache_pathto. The simplest way to purge everything issudo rm -rf /var/cache/nginx/*followed bysudo systemctl reload nginx. For per-URL purging you need thengx_cache_purgemodule or an nginx plugin likefastcgi_cache_purge. - Varnish:
sudo varnishadm ban "req.url ~ ."purges everything. Specific URLs:varnishadm ban "req.url == /specific-page/".
On Hostney, the server-level FastCGI cache is purged automatically whenever you save a post, update a page, or approve a comment – the Hostney Cache plugin integrates WordPress directly with the nginx cache layer, so Layer 4 and Layer 5 act as one unified cache with automatic purge. Manual intervention is not required.
Layer 5: WordPress page cache plugin#
Inside WordPress itself, page-cache plugins store rendered HTML versions of your pages and serve them on subsequent requests without re-running PHP. The common ones:
- WP Rocket (commercial, most popular)
- W3 Total Cache (free + pro)
- WP Super Cache (free, by Automattic)
- LiteSpeed Cache (free, requires LiteSpeed server)
- Hostney Cache (Hostney-specific, integrates with server FastCGI cache)
Each plugin has its own admin page with a “Clear Cache” or “Purge All” button. The typical location:
- WP Rocket: admin bar > WP Rocket menu > Clear Cache; or Settings > WP Rocket > Dashboard > “Clear Cache.”
- W3 Total Cache: admin bar > Performance > Purge All Caches; or Performance > Dashboard > “empty all caches.”
- WP Super Cache: Settings > WP Super Cache > “Delete Cache.”
- LiteSpeed Cache: admin bar > LiteSpeed Cache > Purge All.
- Hostney Cache: admin bar > “Clear All Caches” button, which also purges the nginx FastCGI cache (Layer 4) and the object cache (Layer 6) in one action.
How to confirm the plugin cache is the issue
Most page cache plugins add an HTML comment to cached responses. View the page source (Ctrl+U in most browsers) and search for the plugin’s signature:
- WP Rocket:
<!-- This website is like a Rocket - W3 Total Cache:
<!-- Performance optimized by W3 Total Cache - Hostney Cache: no HTML signature, as purging happens at the nginx layer
If the signature is there and the timestamp is older than your change, the plugin has a stale cache.
Plugin-level pitfalls
Most page cache plugins automatically purge the specific post’s cache when you update it. Where they fall down is on RELATED content: category pages, tag pages, the homepage, archive pages. If you updated a post and the post page itself looks right but the homepage still shows the old excerpt, the homepage cache was not purged as a related page.
Every major plugin has a setting for this (usually “purge related pages” or “automatic URL purging”) – check it is enabled. When in doubt, the “Purge All” button clears everything and takes the guesswork out.
Layer 6: WordPress object cache#
The object cache stores the results of database queries and internal PHP operations in memory. Instead of running
SELECT * FROM wp_options WHERE option_name = 'siteurl'
on every page load, WordPress fetches it from the object cache. Backends vary:
- Database transients (default, no persistent object cache): stored in the
wp_optionstable with expiry timestamps - Redis: external Redis server, persistent across requests and restarts
- Memcached: external Memcached server, persistent across requests but not restarts
- APCu: PHP-process-level cache, fastest but not shared across PHP processes
The object cache is what makes plugin updates sometimes feel like they did not happen – the plugin’s new code is loaded, but the object cache still has cached results from the old code.
How to confirm
WP-CLI shows whether object caching is active:
wp cache supports
This reports the object cache backend in use. If it says “native” or nothing specific, WordPress is using database transients (which behave like a cache but are actually DB rows).
How to clear
WP-CLI (easiest for technical users):
# Flush the object cache
wp cache flush
Transients (if no persistent object cache): the “Delete expired transients” button in most cache plugins, or:
wp transient delete --all
Redis: from the server,
redis-cli FLUSHALL
clears everything. Most WordPress Redis plugins (Redis Object Cache, W3 Total Cache’s Redis backend) have a “Flush Cache” button that calls the same command.
Memcached:
echo "flush_all" | nc localhost 11211
clears everything.
On Hostney: the object cache uses Memcached and is flushed automatically along with the page cache when the “Clear All Caches” button is used. The unified automatic purging approach handles this without manual intervention.
For a deeper explanation of what object caching actually does and how cache misses affect performance, see what is a cache miss and how does it affect performance?
Layer 7: PHP OPcache#
PHP OPcache stores compiled bytecode for every PHP file. Instead of parsing and compiling PHP files on every request, PHP serves the pre-compiled version. This is a pure performance win – nobody notices OPcache until they edit a PHP file and PHP keeps running the old version.
How to confirm
If you edited a plugin or theme PHP file, cleared every other cache, and the change still does not apply, OPcache is the layer to clear.
How to clear
OPcache lives at the php-fpm (or mod_php) level, not in WordPress, so most WordPress plugins cannot touch it directly. Options:
Restart php-fpm (requires SSH):
sudo systemctl restart php8.3-fpm # match your PHP version
This is the most reliable and the most disruptive – every cached PHP file is recompiled on next request.
Programmatic clear (works without restart if you can execute PHP):
opcache_reset();
Put this in a one-off PHP file at your WordPress root, load it once in the browser, then delete the file. Some hosting control panels have a “Reset OPcache” button that calls the same function.
WP-CLI can trigger this too:
wp eval "opcache_reset();"
On shared hosting where you do not have SSH, OPcache typically invalidates automatically when file modification times change – edit a PHP file via SFTP, and PHP detects the new mtime and re-parses it on next request. This only fails when OPcache’s
validate_timestamps
setting is disabled for performance, which is common on production installs. If editing files does not seem to trigger re-parsing, OPcache is almost certainly running with timestamps disabled.
The correct order to purge#
When in doubt, purge top-down:
- Browser cache – hard refresh (Ctrl+Shift+R). Cheapest, fastest test.
- CDN cache – if a CDN is in front of the site, purge its cache for the affected URLs.
- Server page cache (nginx/Varnish) – purge via host’s controls or cache plugin.
- WordPress page cache plugin – “Clear All” in the plugin’s admin.
- Object cache –
wp cache flushor the plugin’s dedicated button. - OPcache – restart php-fpm or call
opcache_reset(). - DNS cache – rare; only if you actually suspect DNS-level stale data.
If you can only clear one layer, start with the CDN – it is the biggest TTL and the one most likely to serve stale content to visitors you are not currently.
Common mistakes#
- Clearing only the plugin cache. WordPress cache plugins do not automatically clear the CDN or browser caches. “I cleared the cache but nothing changed” is almost always because one of the upstream caches was missed.
- Not hard-refreshing after a cache clear. Your own browser is showing you the stale version. Always hard-refresh before declaring the clear “did not work.”
- Clearing caches out of order. Clearing the origin cache (Layer 5) while the CDN (Layer 3) still has old content means visitors still get old content, but the origin is now empty and has to re-render on cache miss – more work, same stale result.
- Using Cloudflare Development Mode as a permanent solution. It turns off caching entirely. Great for debugging, catastrophic for performance if left on.
- Assuming “Purge All” is enough. Some plugins’ “Purge All” only clears THEIR cache, not the CDN integration or the object cache. Verify by re-checking response headers after the purge.
- Editing live site PHP files and expecting instant update. OPcache is the reason this often fails. Deploy via a workflow that touches php-fpm, or accept the wait.
- Purging cache on every visitor complaint. Frequent full purges destroy the cache hit rate and make the site slow. Investigate which layer is actually stale before purging everything.
How Hostney handles this#
Hostney’s approach is to minimize how often you think about cache layers at all. The Hostney Cache plugin listens for content-change events inside WordPress (post save, page update, comment approval, menu edit, theme change) and automatically purges the affected URLs from both the server-level FastCGI cache and the Memcached-backed object cache. No manual “purge cache” click required – publishing a post just works.
When you DO need to purge manually (an external database change bypassed WordPress, a CDN integration needs a reset), the plugin adds a “Clear All Caches” button to the WordPress admin bar that flushes Layers 4, 5, and 6 in a single action. The scope is calculated per-site, so one account’s purge does not affect another account’s cached content. Container isolation means cache state is bounded – no shared cache pool where your purge could be overwritten by a neighboring site’s activity.
The layers Hostney does NOT control for you: the visitor’s browser (Layer 1), the CDN if you have one in front (Layer 3), and DNS (Layer 2). The browser layer is inherent to HTTP – every site has it. The CDN layer is your integration decision – if you put Cloudflare in front of a Hostney-hosted site, you still manage Cloudflare’s cache separately (though the plugin supports webhook integration for major CDNs). DNS is a propagation concern that applies to every host equally.
For PHP OPcache (Layer 7), Hostney’s PHP-FPM containers restart automatically when you change PHP version in the control panel, which clears OPcache as a side effect. Direct PHP file edits via SFTP or Git-based deploys trigger OPcache revalidation via file mtime detection – same mechanism other hosts use, but with sensible defaults (timestamp validation enabled, which keeps file edits working correctly at a small performance cost).
For WordPress users on Hostney, the practical answer to “how do I clear the cache” is usually “publish the post – the cache is already cleared.” For the remaining cases, one button in the admin bar does Layers 4-6 in one action. Layers 1, 2, 3 are the cases that apply to every WordPress site regardless of host.
Summary#
WordPress sites have up to seven cache layers, and clearing the wrong one is the reason “I already cleared the cache” does not fix the problem. Work top-down from the browser: hard-refresh first, then CDN, then server-level cache, then WordPress page cache plugin, then object cache, then OPcache, then DNS. Each layer has its own controls and signatures in response headers – use
curl -I
to confirm which layer is serving the stale copy before purging anything. Most major caching setups automatically purge on save, so frequent manual purging is a sign of either a misconfigured plugin or an external cache (CDN, separate page cache) that is not integrated with WordPress’s save hooks. On Hostney, the server cache, WordPress page cache, and object cache are purged automatically together when content changes, and a single admin-bar button handles the rare manual-purge case – leaving the CDN, browser, and DNS layers as the only ones that ever need conscious attention.