WordPress image cache-busting solves a deceptively simple problem: you replace or modify an image on the server, but visitors continue seeing the old version.
The file may already be correct in wp-content/uploads. WordPress may already know about the new image. You may even open the file through FTP and confirm that the pixels have changed.
Yet the browser stubbornly displays yesterday’s version.
This usually happens because the image URL has not changed and one or more caching layers still consider the previously downloaded response reusable.
Cache busting gives the updated resource a different effective URL so browsers, CDNs and other caches treat it as a new resource instead of reusing the old cached response.
This guide explains how WordPress image cache-busting works, why replacing a file does not automatically invalidate every cache, how query-string versions differ from filename versioning, how HTTP caching headers such as Cache-Control, ETag and Last-Modified fit into the picture, and when you should purge a CDN instead of changing an image URL.
What is browser caching?
When a browser downloads an image such as:
https://example.com/wp-content/uploads/2026/08/hero.jpg
it may store a local copy.
When the page needs the same URL again, the browser may be able to reuse that copy instead of downloading the entire image again.
This is good.
Without caching, websites would repeatedly transfer the same:
- images;
- CSS files;
- JavaScript;
- fonts;
- icons;
- other static resources.
The result would be slower pages and unnecessary bandwidth consumption.
Caching is supposed to make WordPress faster
Imagine a site logo that weighs:
120 KB
and appears on every page.
Without effective caching, a visitor navigating through ten pages might repeatedly request the same bytes.
With caching:
First request
→ download logo
Later requests
→ reuse cached logo
That is precisely the behavior you normally want.
The problem appears when the file changes
Suppose:
Monday:
hero.jpg = old photograph
Tuesday:
hero.jpg = new photograph
but the URL remains:
https://example.com/wp-content/uploads/2026/08/hero.jpg
A cache that already has a fresh response for that URL may have no reason to ask the origin server for another copy yet.
The browser does not know that you edited the file
From the browser’s perspective, it previously requested:
/wp-content/uploads/2026/08/hero.jpg
and received a valid response.
Now the page requests:
/wp-content/uploads/2026/08/hero.jpg
again.
Same URL.
If the cached response is still considered fresh, reusing it is perfectly rational behavior.
The browser is not being difficult. For once, the computer is actually following instructions.
What is cache busting?
Cache busting means changing the resource URL when the resource changes.
Instead of:
hero.jpg
you might request:
hero.jpg?v=2
or:
hero.jpg?ver=1724847821
or use a versioned filename:
hero.a81f93c2.jpg
The cache now sees a different resource identifier.
The basic cache-busting principle
Old content
→ /hero.jpg?v=1
New content
→ /hero.jpg?v=2
Even if both requests ultimately map to the same underlying file, the URLs are different from the cache’s perspective.
Why does changing the URL work?
HTTP caches generally associate stored responses with request information that includes the target URI.
Therefore:
/hero.jpg?v=1
and:
/hero.jpg?v=2
are distinct cache keys in normal caching architectures.
The second request cannot simply reuse a response stored only for the first URL.
Cache busting does not mean disabling caching
This distinction is extremely important.
A poor response to stale images is often:
Disable caching everywhere.
That solves freshness by throwing away the performance benefits of caching.
Cache busting instead aims for:
Cache aggressively
+
change URL when content changes
This gives you both performance and reliable updates.
Immutable URLs are ideal for static resources
The MDN HTTP caching guide describes URL versioning as a standard cache-busting strategy for static resources.
The ideal model is:
hero.v1.jpg
→ never changes
hero.v2.jpg
→ new content
Each URL identifies one stable representation.
That lets the server safely tell browsers to cache each version for a long time.
Cache-Control determines how responses may be cached
The HTTP:
Cache-Control
response header provides caching instructions to browsers and shared caches.
The MDN Cache-Control reference documents directives used to control this behavior.
A static image might receive something conceptually like:
Cache-Control: public, max-age=31536000
This allows the response to remain fresh for a long period.
Long cache lifetimes work best with versioned resources
If:
/hero.a81f93c2.jpg
will never change, caching it for a year is relatively safe.
When the image changes, publish:
/hero.c902d1a7.jpg
instead.
The old cached object remains valid for old references, while new pages request the new URL.
The immutable directive goes one step further
A versioned static resource can be served with:
Cache-Control: public, max-age=31536000, immutable
The immutable directive tells supporting clients that the resource will not change while it is fresh.
This pattern works particularly well when changing content always produces a new URL.
What does no-cache actually mean?
One of HTTP’s finest contributions to human confusion is the name:
no-cache
It does not mean:
Never store this response.
Instead, it generally means that a stored response must be validated before it is reused.
For example:
Cache-Control: no-cache
can allow storage while requiring revalidation with the origin before reuse.
no-store means something different
If you genuinely do not want the response stored, the relevant directive is:
Cache-Control: no-store
This distinction is documented in the Cache-Control specification guidance on MDN.
Disabling image caching is usually unnecessary
For ordinary public WordPress images, this:
Cache-Control: no-store
is usually a poor default.
Images are excellent caching candidates because they are static resources and often account for a large percentage of transferred page weight.
Versioning is generally more efficient than forbidding storage.
There are several caching layers on a WordPress site
A typical request can pass through:
WordPress / origin
↓
Web server
↓
Reverse proxy
↓
CDN
↓
Browser cache
Some sites add even more layers.
Browser cache
The visitor’s browser can store an image locally.
This is the most familiar caching layer and often the first suspect when only one person still sees the old image.
CDN cache
A CDN may store the image at edge locations.
For example:
Origin:
example.com/wp-content/uploads/hero.jpg
CDN:
cdn.example.com/wp-content/uploads/hero.jpg
You may replace the origin file successfully while the CDN continues serving its cached copy.
Reverse-proxy cache
Hosting platforms may place systems such as:
- Nginx caching;
- Varnish;
- other reverse proxies;
- managed hosting edge caches;
between WordPress and the visitor.
Page cache can preserve an old image URL
Suppose you correctly change:
hero.jpg?v=1
to:
hero.jpg?v=2
inside WordPress.
But a full-page cache still serves old HTML containing:
hero.jpg?v=1
The image cache itself may be working perfectly.
The stale component is the HTML page that tells the browser which image URL to request.
This is why cache debugging requires identifying the layer
When an old image appears, ask:
Is the image file stale?
Is the HTML reference stale?
Is the CDN stale?
Is the browser stale?
Is WordPress generating an old URL?
Purging random caches until the problem disappears works surprisingly often, but it teaches you absolutely nothing about which system was responsible.
WordPress attachment URLs normally remain stable
WordPress can retrieve the URL of an attachment using wp_get_attachment_url().
A normal attachment might return:
https://example.com/wp-content/uploads/2026/08/logo.png
If the physical content behind that path changes while the URL remains identical, caches may continue associating the URL with the old response.
This becomes important when replacing WordPress media
Suppose a company logo is used on:
- the homepage;
- the footer;
- landing pages;
- blog posts;
- structured templates.
Deleting the attachment and uploading a new one can produce a different URL, but it may also break existing references.
Replacing the file while preserving its attachment identity avoids that problem.
For the broader comparison, see Replacing vs. re-uploading WordPress media.
TheOneWP Media Replace preserves file identity
TheOneWP Media Replace is designed to replace a file associated with an existing attachment rather than forcing you to delete and re-upload it.
This is useful because references to that attachment can remain intact.
But preserving the same URL creates an obvious caching question:
How does the browser know the bytes changed?
Cache busting solves the stable-URL problem
Conceptually, an attachment URL can remain:
https://example.com/wp-content/uploads/logo.png
while the URL used by the frontend becomes:
https://example.com/wp-content/uploads/logo.png?v=1724847821
After another replacement:
https://example.com/wp-content/uploads/logo.png?v=1724934417
The underlying attachment relationship remains stable while the effective browser request changes.
Query-string cache busting
One common strategy is to append a query parameter.
For example:
image.jpg?v=1
then:
image.jpg?v=2
or:
image.jpg?ver=20260828
The parameter value should change only when the resource changes
A useful cache-busting version can come from:
- an application version;
- a deployment version;
- a file modification timestamp;
- a content hash;
- a stored attachment revision value.
The important requirement is that a changed resource receives a changed URL.
Do not use a random value on every page load
This technically busts the cache:
image.jpg?v=839271
image.jpg?v=172913
image.jpg?v=553812
but it defeats caching entirely because every request appears new.
You have successfully implemented cache busting by making the cache practically useless. Humanity survives another optimization project.
A timestamp can be useful when tied to file modification
For example:
$version = filemtime( $path );
could conceptually produce:
image.jpg?ver=1787918412
If the file modification time changes only when the file changes, the URL remains stable between modifications.
Do not confuse filemtime with the current time
This:
filemtime( $path )
can provide deterministic versioning based on the file.
This:
time()
changes constantly.
Using:
image.jpg?ver=<current-time>
on every request forces a new cache key every time and eliminates most of the benefit.
WordPress itself uses version parameters for cache busting
This is not some exotic workaround invented for image replacement.
The official wp_enqueue_script() API includes a version parameter specifically used for cache busting.
A script can be enqueued conceptually like:
wp_enqueue_script(
'theme-app',
'/assets/app.js',
array(),
'2.4.1'
);
which can produce a request containing a version query parameter.
The same principle applies to stylesheets
WordPress asset management can version CSS and JavaScript because those resources have exactly the same fundamental caching problem:
same URL
+
new bytes
=
possible stale cache
Images are no different from the cache’s perspective.
Filename versioning is another strategy
Instead of:
hero.jpg?v=2
you can use:
hero.v2.jpg
or:
hero.8b17a2f4.jpg
Content hashes are especially robust
A build system might calculate a hash from the file contents.
For example:
Old file:
hero.a817bc93.jpg
New file:
hero.46d0c1aa.jpg
If the contents change, the hash changes.
If the contents stay the same, the hash stays the same.
Hash-based filenames make immutability explicit
This allows an architecture like:
hero.a817bc93.jpg
Cache-Control: public, max-age=31536000, immutable
When content changes, publish another filename instead of mutating the old resource.
Why not always use hashed filenames in WordPress?
Because WordPress media attachments are not always managed through a frontend build pipeline.
WordPress expects media to have relationships involving:
- attachment IDs;
- attachment metadata;
- generated image sizes;
- post content;
- featured images;
- theme settings;
- plugin references.
Renaming a media file every time it changes can require updating those relationships.
Query strings are useful when attachment identity must stay stable
This makes query-string versioning particularly attractive for replacement workflows:
Physical attachment:
logo.png
Effective URL:
logo.png?v=42
The file identity remains understandable while the browser gets a new cache key.
Are query strings cacheable?
Modern HTTP caches can cache resources containing query strings.
The important consideration is how your specific:
- CDN;
- reverse proxy;
- hosting platform;
- cache configuration;
uses query parameters in its cache key.
CDN configuration can change query-string behavior
One CDN may distinguish:
image.jpg?v=1
image.jpg?v=2
while a custom cache policy could be configured to ignore selected query parameters.
If your cache layer ignores the parameter you use for versioning, query-string cache busting will not behave as intended at that layer.
Always understand the CDN cache key
When using query-string versioning behind a CDN, verify whether the CDN:
- includes all query strings;
- includes selected query strings;
- ignores query strings;
- normalizes parameter order;
- uses custom cache rules.
Changing a query parameter does not physically duplicate the file
These:
hero.jpg?v=1
hero.jpg?v=2
hero.jpg?v=3
can all point to:
hero.jpg
on the origin filesystem.
The query parameter primarily changes the request identity used by caching systems.
That differs from filename versioning
With filename versioning:
hero.v1.jpg
hero.v2.jpg
you may genuinely have two physical files.
This can be useful for immutable deployment assets but creates different storage and lifecycle considerations.
What is an ETag?
An ETag is an HTTP response validator identifying a particular representation of a resource.
A response might contain:
ETag: "33a64df551425fcc55e4d42a148795d9"
When the cached response becomes stale, the browser can ask whether that representation is still current.
ETag enables conditional requests
The browser may send:
If-None-Match: "33a64df551425fcc55e4d42a148795d9"
If the resource has not changed, the server can respond:
304 Not Modified
without retransmitting the entire image body.
A changed ETag signals changed content
If the current resource representation has a different ETag, the server can return:
200 OK
with the new image.
ETag is validation, not exactly the same as cache busting
Compare:
Cache busting:
Change the URL
Validation:
Ask whether cached content for same URL is still current
Both can solve stale-resource problems, but they operate differently.
Last-Modified is another validator
The Last-Modified header tells the client when the origin believes the resource was last changed.
For example:
Last-Modified: Fri, 28 Aug 2026 08:32:00 GMT
A browser can later send:
If-Modified-Since: Fri, 28 Aug 2026 08:32:00 GMT
The server can answer with 304 Not Modified
If nothing changed:
HTTP/1.1 304 Not Modified
The browser can reuse its cached body without downloading the image again.
ETag and Last-Modified reduce unnecessary transfers
A healthy caching strategy can therefore combine:
Cache freshness
+
validators
+
versioned URLs where appropriate
These mechanisms complement rather than replace each other.
Why can the old image remain even when Last-Modified changed?
If the cached response is still fresh according to:
Cache-Control: max-age=...
the browser may not need to perform validation yet.
It can reuse the cached response without asking the server whether the file changed.
This is precisely where changing the URL is powerful.
Cache busting does not wait for freshness to expire
If the page changes from:
hero.jpg?v=1
to:
hero.jpg?v=2
the browser needs the new URL now.
It does not need to wait for the cached v=1 response to become stale.
What happens to the old cached image?
It may remain in the browser or CDN cache until:
- it expires;
- it is evicted;
- the cache is cleared;
- storage pressure removes it.
That is normally harmless.
The current page simply stops requesting the old version.
Cache busting is therefore not cache deletion
This:
hero.jpg?v=2
does not necessarily delete:
hero.jpg?v=1
from every cache on Earth.
It makes new pages request a different resource identifier.
Cache purging is different
A purge explicitly tells a caching layer to remove or invalidate a cached object.
For example:
Purge:
https://cdn.example.com/hero.jpg
Afterward, the next request normally needs to fetch or revalidate the resource.
Cache busting vs. cache purging
CACHE BUSTING
Change:
Resource URL
Example:
hero.jpg?v=1
→ hero.jpg?v=2
CACHE PURGING
Change:
Stored cache state
Example:
Remove cached hero.jpg
from CDN edge
When is purging useful?
Purging is useful when:
- the public URL cannot change;
- you need the old representation removed immediately;
- a CDN ignores your version parameter;
- stale HTML still references an old resource;
- a sensitive file was accidentally cached;
- you are troubleshooting a caching layer.
When is versioning preferable?
Versioning is particularly useful for ordinary static assets where:
- performance matters;
- long cache lifetimes are desirable;
- content changes occasionally;
- the generated URL can change when content changes.
Use both when the architecture requires it
A replacement workflow might:
- replace the source file;
- regenerate thumbnails;
- change an attachment version;
- purge CDN copies of old stable URLs;
- purge page cache so new HTML contains new versions.
There is no rule saying one cache strategy must heroically solve every layer by itself.
Thumbnail regeneration can create another caching problem
Suppose WordPress regenerates:
photo-768x512.jpg
and writes new content to the same filename.
The filesystem is correct.
But a browser or CDN may already have:
photo-768x512.jpg
cached.
The new thumbnail can therefore exist while visitors continue seeing the previous derivative.
Regeneration and cache invalidation are separate
For the full image-generation process, see What happens when WordPress regenerates thumbnails.
The important relationship is:
Thumbnail regeneration
→ changes image files
Cache busting / purge
→ changes which image response visitors receive
Responsive images can contain several cached URLs
WordPress may output:
<img
src="photo-768x512.jpg"
srcset="
photo-300x200.jpg 300w,
photo-768x512.jpg 768w,
photo-1024x683.jpg 1024w
"
>
The browser can choose among those candidates.
Updating only one URL may not update every candidate
Suppose you version:
photo-768x512.jpg?v=2
but the srcset still contains:
photo-300x200.jpg
photo-1024x683.jpg
A browser may choose one of those unversioned candidates instead.
For responsive images, cache-busting logic needs to consider the actual URLs emitted in srcset, not merely the visible src attribute.
Different devices may therefore appear to show different versions
A desktop browser might choose:
photo-1024x683.jpg
while a mobile device chooses:
photo-300x200.jpg
If only one derivative was refreshed correctly, users can report apparently inconsistent results.
Inspect the actual image request
Browser developer tools can reveal:
- the requested URL;
- query parameters;
- response status;
Cache-Control;ETag;Last-Modified;- CDN-specific headers;
- whether the resource came from memory or disk cache.
Do not diagnose caching from the address bar alone
The HTML may contain one URL while the browser selects another from srcset.
Inspect the Network panel and identify the exact resource that supplied the pixels you are looking at.
curl can help inspect HTTP caching headers
For example:
curl -I https://example.com/wp-content/uploads/2026/08/hero.jpg
You might receive:
HTTP/2 200
content-type: image/jpeg
cache-control: public, max-age=31536000
etag: "abc123"
last-modified: Fri, 28 Aug 2026 08:32:00 GMT
Now you have actual evidence about the caching policy instead of the traditional technical methodology of refreshing aggressively and becoming annoyed.
Look for cache-status headers
CDNs and proxies may add headers indicating whether a response was:
HIT
MISS
BYPASS
STALE
REVALIDATED
The exact header names depend on the service.
These are extremely useful when determining whether the stale response comes from an intermediary rather than WordPress itself.
A hard refresh is useful for diagnosis, not as a production strategy
A browser hard reload can bypass or revalidate cached resources depending on the browser and request.
If a hard refresh fixes the image, browser caching is probably involved.
But telling every visitor:
Please press Ctrl+Shift+R
is not a cache-invalidation architecture.
Incognito mode is not a perfect cache test
A private browsing session can help isolate normal browser cache state, but it does not bypass:
- CDN caches;
- reverse proxies;
- server caches;
- stale page HTML served upstream.
If incognito still shows the old image, investigate further upstream.
Test the origin separately when using a CDN
If possible, compare:
Origin response
vs.
CDN response
If the origin serves the new image while the CDN serves the old one, WordPress is probably not the problem.
Do not repeatedly replace the image while debugging
If you replace:
version A
→ version B
→ version C
→ version D
during troubleshooting, you create more variables.
First determine which URL and caching layer is serving the stale response.
Page builders can add their own complications
A page builder may store:
- attachment IDs;
- direct image URLs;
- generated CSS;
- responsive image variants;
- cached frontend markup.
Replacing an attachment may therefore require clearing builder-generated assets or page caches in addition to handling the image itself.
CSS background images can be cached too
An image may be loaded through:
background-image:
url('/wp-content/uploads/hero.jpg');
instead of an HTML <img>.
In that case you may have two relevant cached resources:
CSS file
+
image file
A stale stylesheet can keep requesting an old image URL
Suppose the new CSS should contain:
hero.jpg?v=2
but the browser still has an old stylesheet containing:
hero.jpg?v=1
Updating only the image cache will not fix the stale stylesheet reference.
Version CSS and JavaScript too
This is why WordPress’s asset APIs include version parameters.
A theme can use a deterministic version based on a file modification time, for example:
wp_enqueue_style(
'theme-style',
get_stylesheet_uri(),
array(),
filemtime( get_stylesheet_directory() . '/style.css' )
);
The principle is identical to image cache busting.
Do not use the WordPress version as your custom asset version accidentally
WordPress enqueue APIs can default to the installed WordPress version when no explicit asset version is supplied in certain configurations.
That means your custom:
app.js
might change while its version query parameter remains tied to WordPress itself.
For custom assets that change independently, use a version representing the actual asset or release.
Deployment versions are useful for site-wide assets
A project may define:
ASSET_VERSION = '2026.08.28.1'
and use it for:
- CSS;
- JavaScript;
- selected static images;
- generated bundles.
A new deployment increments the version.
Content hashes are better when files change independently
A global deployment version invalidates every asset even when only one file changed.
Content hashing allows:
app.7c91.js
logo.a831.svg
hero.9d14.webp
Only the resource whose content changes receives a new identifier.
WordPress Media Library images are different from build assets
Theme assets such as:
/theme/assets/logo.svg
are often controlled by developers and deployments.
Media Library files such as:
/uploads/2026/08/customer-photo.jpg
can be changed by editors through WordPress.
The versioning strategy needs to account for who changes the resource and how the application knows that it changed.
Attachment metadata can provide a place for version state
A replacement system can associate a version or timestamp with an attachment.
Conceptually:
Attachment ID:
842
File:
logo.png
Media version:
1787918412
Frontend URL:
logo.png?v=1787918412
When the attachment is replaced, the version changes.
Version only when the content changes
This is the ideal behavior:
Request 1:
logo.png?v=10
Request 2:
logo.png?v=10
Request 3:
logo.png?v=10
File replaced
Request 4:
logo.png?v=11
The first three requests benefit from caching.
The fourth immediately addresses the new resource version.
What about WordPress generated thumbnails?
An attachment can have:
logo.png
logo-150x150.png
logo-300x300.png
logo-768x768.png
If the source image is replaced and thumbnails are regenerated, every generated file whose bytes changed potentially has the same caching issue.
Versioning should account for attachment derivatives
A shared attachment version can conceptually produce:
logo.png?v=11
logo-150x150.png?v=11
logo-300x300.png?v=11
logo-768x768.png?v=11
This lets all derivatives participate in the same invalidation event.
Image Sizes List helps identify the derivatives involved
TheOneWP Image Sizes List helps inspect the registered and generated image-size landscape around WordPress attachments.
That becomes useful when a replacement appears correct at one size but stale at another.
Image optimization introduces more possible variants
An optimization workflow may create:
image.jpg
image.webp
image.avif
along with several dimensions of each format.
TheOneWP Image Optimizer addresses image optimization and modern delivery formats, which means cache invalidation needs to consider whichever representation is actually served to the browser.
Do not assume the browser is receiving the JPEG you inspected
You may inspect:
hero.jpg
and see the new image.
But the browser could actually receive:
hero.webp
from an optimization layer.
If the WebP version is stale, replacing the JPEG alone does not solve what the visitor sees.
CDN image transformation services add another layer
Some CDNs transform a source URL into variants based on:
- width;
- height;
- quality;
- format;
- device characteristics.
Conceptually:
/hero.jpg?width=800&format=webp
may itself be cached independently.
Changing the source does not guarantee transformed variants disappear immediately
If an image CDN has cached derived representations, you may need:
- source versioning;
- CDN invalidation;
- transformation cache purging;
- a new transformation URL.
Be careful when combining version and transformation query parameters
For example:
hero.jpg?w=800&format=webp&v=11
works only if the caching and transformation layers include the relevant version parameter in their cache logic.
Signed URLs are a different concept
A URL such as:
file.jpg?expires=...&signature=...
may change because of authorization rather than cache busting.
Do not assume every changing query string exists to invalidate cache.
For the distinction between public and protected media delivery, see Hiding vs. restricting access to WordPress media.
Cache busting is not a security mechanism
This:
private-report.pdf?v=4829
does not make the document private.
It merely changes the URL used for caching.
If anyone can request that URL successfully, the resource remains publicly accessible.
Changing image URLs can have SEO implications
Public images can be discovered and indexed independently.
Constantly creating unnecessary new physical image URLs may fragment signals and create old URLs that remain accessible.
This is another reason query-string versioning can be attractive when the underlying canonical media identity should remain stable.
Do not version URLs unnecessarily on every request
Search crawlers, CDNs and browsers all benefit from stable resource identities.
The ideal rule remains:
Same content
→ same version
Changed content
→ new version
Cache busting should be deterministic
Good:
hero.jpg?v=42
until the file changes.
Then:
hero.jpg?v=43
Bad:
hero.jpg?v=random-value-on-every-request
What happens when you remove the version parameter?
If users previously requested:
hero.jpg?v=42
and your HTML later requests:
hero.jpg
that unversioned URL has its own cache history.
A browser or CDN may already contain an older response for it.
Removing cache-busting parameters can therefore appear to resurrect stale content.
Keep your versioning strategy consistent
Avoid alternating unpredictably between:
hero.jpg
hero.jpg?v=1
hero.jpg?v=2
hero.jpg
Choose a clear resource-versioning strategy and apply it consistently.
Page-cache invalidation remains necessary when HTML changes
Suppose the attachment version changes correctly from:
?v=10
to:
?v=11
but cached HTML still contains ?v=10.
The visitor will continue requesting the old version until the page cache refreshes.
Think of cache invalidation as a dependency chain
Image changed
↓
Image version changed
↓
HTML must reference new version
↓
Page cache must expose new HTML
↓
CDN must handle new URL correctly
↓
Browser requests new resource
If one layer remains stale, the update can appear incomplete.
Replacing vs. re-uploading changes the caching problem
Re-uploading may naturally produce:
logo-2.png
instead of:
logo.png
That automatically creates a new URL but requires references to be updated.
Replacing preserves:
logo.png
which keeps references intact but means cache invalidation deserves deliberate handling.
This tradeoff is covered in Replacing vs. re-uploading WordPress media.
When should you use query-string cache busting?
It is useful when:
- the physical filename should remain stable;
- attachment references should remain intact;
- the cache layer respects the version parameter;
- you can reliably change the version when the content changes;
- you want long-lived caching between updates.
When should you prefer versioned filenames?
They are particularly useful for:
- compiled frontend assets;
- theme bundles;
- deployment-controlled images;
- immutable static resources;
- CDN-heavy architectures.
When should you purge instead?
Purging may be preferable when:
- the public URL cannot change;
- you need immediate invalidation of the exact URL;
- the CDN ignores query parameters;
- an old resource must stop being served;
- you are correcting an accidental exposure.
When should you use HTTP validators?
ETag and Last-Modified are valuable when the same URL can legitimately change and clients should efficiently determine whether their cached representation remains current.
They can reduce bandwidth through:
304 Not Modified
responses.
A practical WordPress image update workflow
- Identify the attachment being changed.
- Determine whether the URL will remain the same.
- Replace or update the source image.
- Regenerate image sub-sizes if required.
- Update the deterministic media version.
- Ensure
srcandsrcsetreceive the correct version where applicable. - Purge page cache if rendered HTML changed.
- Purge CDN objects if the architecture requires it.
- Check optimized WebP or AVIF variants.
- Test the exact production URL.
- Inspect HTTP response headers.
- Verify from another browser or network.
A practical cache debugging workflow
If an old WordPress image refuses to disappear, work through the layers instead of randomly clearing everything.
1. Open exact image URL
↓
2. Confirm origin file contents
↓
3. Inspect requested URL in DevTools
↓
4. Check query-string version
↓
5. Check Cache-Control
↓
6. Check ETag / Last-Modified
↓
7. Check CDN cache status
↓
8. Check responsive image candidate
↓
9. Check WebP / AVIF variant
↓
10. Check cached page HTML
Common WordPress image cache-busting mistakes
Using time() on every request
This forces constant new URLs and defeats caching rather than managing it.
Changing the image but not its version
A long-lived cached response can continue being reused.
Changing the version but leaving stale page HTML
The browser never learns that the new version exists.
Purging WordPress cache but ignoring the CDN
The edge may continue serving the old image.
Purging the CDN but ignoring browser cache
A browser may still have a fresh local response.
Testing only the original image
The frontend may use a generated thumbnail instead.
Ignoring srcset
The browser may choose a different derivative than the one you inspected.
Ignoring modern image formats
You may update JPEG while visitors receive a stale WebP or AVIF.
Assuming no-cache means no storage
no-cache permits storage but requires validation before reuse.
Disabling caching entirely
That trades one freshness problem for worse performance.
Using versioned URLs without checking CDN query-string rules
A cache configured to ignore the version parameter can defeat the strategy.
Using cache busting as access control
A changing URL does not make public media private.
Replacing the same URL repeatedly while debugging
You make it harder to identify which cached representation is actually being served.
WordPress image cache-busting checklist
- Identify whether the image URL remains stable after an update.
- Use deterministic versioning rather than random values.
- Change the version only when the resource changes.
- Consider file modification timestamps where appropriate.
- Consider content hashes for build-controlled assets.
- Use long cache lifetimes for truly versioned immutable resources.
- Understand the difference between
no-cacheandno-store. - Use
ETagandLast-Modifiedappropriately. - Check browser cache.
- Check page cache.
- Check reverse-proxy cache.
- Check CDN cache.
- Check CDN query-string behavior.
- Check
srcsetcandidates. - Check generated WordPress image sizes.
- Check WebP and AVIF versions.
- Check image-transformation services.
- Purge cache when URL versioning cannot solve the requirement.
- Do not disable all static-asset caching merely to avoid stale images.
- Do not use random query parameters on every request.
- Test the actual production response headers.
Related WordPress media and caching guides
For the wider image-management, caching and performance cluster, continue with:
- What happens when WordPress regenerates thumbnails
- Replacing vs. re-uploading WordPress media
- Keeping a WordPress media library organized at scale
- Hiding vs. restricting access to WordPress media
- CDN vs. self-hosted assets in WordPress
- Why third-party embeds slow down WordPress
- Reducing WordPress front-end page weight
- WordPress staging site best practices
- Media Replace
- Image Sizes List
- Image Optimizer
Final thoughts
WordPress image cache-busting is not about preventing images from being cached. It is about making caching predictable when image content changes.
The most useful principle is simple:
Same content
→ same URL
Changed content
→ new effective URL
For Media Library workflows where the physical filename and attachment identity should remain stable, a deterministic query-string version can provide that new effective URL without forcing every existing attachment reference to be replaced.
For build-controlled static assets, versioned or content-hashed filenames are often even stronger because each URL can represent immutable content and receive a very long cache lifetime.
HTTP validators such as ETag and Last-Modified solve a related but different problem by allowing browsers to efficiently check whether a cached response for the same URL is still current. CDN purging solves another problem again by explicitly invalidating stored edge responses.
TheOneWP Media Replace makes this distinction particularly relevant because preserving an attachment and its references is useful only if visitors can reliably receive the new file after replacement. Image Sizes List helps identify the derivatives involved, while Image Optimizer adds another layer of image representations that should be considered when debugging delivery.
When an old WordPress image refuses to disappear, therefore, do not immediately blame WordPress, the browser, the CDN, the hosting provider and whichever colleague happens to be nearest.
Find the exact URL being requested, identify which cache owns the stale response and invalidate the correct layer. Caching becomes considerably less mysterious once you stop treating every cache on the internet as if it were one enormous button labelled “Clear.”

