Opens in a new tab
  1. Home
  2. Guides
  3. Utilities
Utilities guide

WordPress image cache-busting, explained

Learn why WordPress images can remain cached after replacement and how versioned URLs, HTTP validators, CDN purges and deterministic cache busting solve the problem.

  • Updated August 28, 2026
  • 20 min read
  • WordPress guide

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:

  1. replace the source file;
  2. regenerate thumbnails;
  3. change an attachment version;
  4. purge CDN copies of old stable URLs;
  5. 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

  1. Identify the attachment being changed.
  2. Determine whether the URL will remain the same.
  3. Replace or update the source image.
  4. Regenerate image sub-sizes if required.
  5. Update the deterministic media version.
  6. Ensure src and srcset receive the correct version where applicable.
  7. Purge page cache if rendered HTML changed.
  8. Purge CDN objects if the architecture requires it.
  9. Check optimized WebP or AVIF variants.
  10. Test the exact production URL.
  11. Inspect HTTP response headers.
  12. 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-cache and no-store.
  • Use ETag and Last-Modified appropriately.
  • Check browser cache.
  • Check page cache.
  • Check reverse-proxy cache.
  • Check CDN cache.
  • Check CDN query-string behavior.
  • Check srcset candidates.
  • 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:

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.”

Simplify your WordPress stack

A modular WordPress toolkit. 104 focused tools.

Ultimately, you can build cleaner workflows, maintain fewer plugins and enable only the features each website actually needs.