Skip to content

Image Editor reload appends stacking cache timestamp and doesn't reflect edits behind a CDN #595

@suprimsimple

Description

@suprimsimple

Description

When you edit an image with the asset Image Editor from inside a CKEditor field, _reloadImage() in src/web/assets/ckeditor/src/image/imageeditor/imageeditorcommand.js builds a cache-busted src from srcInfo.baseSrc. But baseSrc comes from the greedy (.*) in _srcInfo()'s regex:

const match = src.match(/(.)#asset:(\d+)(?::transform:([a-zA-Z][a-zA-Z0-9_]))?/);
// baseSrc = match[1] → everything before "#asset:", INCLUDING any existing query
So baseSrc already contains the previously-appended ?, and _reloadImage() appends another one each time:

let newSrc = image.srcInfo.baseSrc + '?' + new Date().getTime() + '#asset:' + image.srcInfo.assetId;
The query string stacks on every edit, producing malformed URLs (a URL can only have one ?):

…/MT-3_2026-06-09-020259_hbsn.jpeg?0?1780971727673?1780971779690?1780971926837#asset:1395663
This stacked URL is then persisted into the stored field content.

A second, related problem: even with a single valid cache-buster, the editor swaps the src immediately on save with no allowance for CDN propagation. When assets are served through a CDN that caches by path / doesn't vary on the query string and invalidates asynchronously (e.g. CloudFront + AWS Serverless Image Handler, which is a very common Craft setup), the reload fetches the still-cached pre-edit image, so the edit doesn't visibly update — often requiring repeated saves or a hard refresh.

For reference, Craft core avoids this for its own thumbnails by deriving a stable, content-based buster from the asset's modified time (AssetsHelper::revUrl() / revParams() → ?v=), rather than appending a fresh timestamp onto whatever is already in the src.

imageeditorcommand.js

Steps to reproduce

  • Insert an image asset into a CKEditor field and save the entry.
  • Select the image → open the Image Editor → crop/rotate → Save.
  • Repeat step 2 two or three more times.
  • View the image src (editor "Source" view or the saved field content): the query string accumulates — ???… — instead of being replaced.
  • (If assets are behind a CDN like CloudFront/Serverless Image Handler) the edited image also fails to display the change until multiple saves / a hard refresh, because the stale CDN copy keeps being served.

Additional info
Craft version: 5.10.5
PHP version: 8.2.28
Database driver & version: MySQL 8.0.40
Plugins & versions:
CKEditor (craftcms/ckeditor), 5.x branch (CKEditor 5, ckeditor5 ^48.2.0)
AWS S3 (craftcms/aws-s3) 2.3.0 — assets on S3 served via CloudFront + Serverless Image Handler

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions