How-To: Flush image style derivatives after replacing a file's content
Audience: developers and content managers in the monorepo Last reviewed: 2026-09-21
Goal
Understand why a managed file's already-generated image style derivatives
(the resized/converted copies under sites/default/files/styles/...) don't
update just because the file's own content changed, and how to force them
to regenerate with drush image:flush.
The gotcha
Drupal generates an image style derivative once, the first time it's
requested, and caches it on disk under public://styles/<style>/public/....
After that, the derivative route only checks whether that cached file
exists — it does not compare it against the source file's content or
modification time. If you replace a managed file's binary data in place
(same file id, same URI — e.g. via \Drupal::service('file.repository')
->writeData($data, $existing_uri, FileSystemInterface::EXISTS_REPLACE)),
every image style's derivative that was already generated from the old
content keeps being served unchanged, even though the source file
underneath it is now different. Nothing errors; the page just quietly
keeps showing the old image.
This bit us concretely 2026-09-21: a hero carousel image was re-imported at
a much higher resolution (a Kaltura thumbnail, originally fetched at
Kaltura's low-res default), but the site kept showing the old, tiny
version — because the wide image style's derivative had already been
generated (and cached) from the original low-res file during earlier
testing, before the re-import.
Prerequisites
- DDEV running (
ddev start), or the equivalentdrushaccess on dev-0/staging.
Steps
- Identify the image style(s) whose derivatives need to be cleared. Check
the field's display config (
core.entity_view_display.*.yml) or just inspect a rendered<img>src for the/styles/<name>/segment. - Flush that style's cached derivatives:
Comma-separate multiple style names in one call:
ddev drush image:flush wideRun with no arguments to get an interactive picker, or flush every style site-wide (heavier, regenerates everything on next view):ddev drush image:flush wide,large,thumbnailddev drush image:flush --all - Derivatives regenerate lazily, the next time each one is actually
requested (by a page view). To eagerly regenerate one specific
derivative right away instead of waiting on that:
ddev drush image:derive wide public://path/to/file.jpg
Verify
Fetch the derivative directly and check its real dimensions/size, rather than trusting a browser tab that may itself be caching the old image:
# Find the current itok query-string token for a given source file --
# grep a fresh page render for the file's own derivative URL rather than
# guessing the token; it's stable for a given file+style, not random.
curl -sk https://mandala.ddev.site:8443/home -o /tmp/page.html
grep -o 'your-file-name.jpg.webp?itok=[^"]*' /tmp/page.html
# Fetch and inspect the actual derivative
curl -sk "https://mandala.ddev.site:8443/sites/default/files/styles/wide/public/your-file-name.jpg.webp?itok=<token>" -o /tmp/check.webp
file /tmp/check.webp # shows real pixel dimensions and encoding
A derivative regenerated from genuinely new source content will differ noticeably in file size and reported dimensions from the stale one.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Old image still shows after replacing a file's content, even after drush cache:rebuild |
Image style derivative cache is separate from Drupal's render/config cache; cache:rebuild doesn't touch it |
drush image:flush <style> |
Still looks the same after image:flush |
Your own browser cached the derivative at that exact URL (same itok, since the token is stable per file+style, not random) |
Hard-reload / fetch the derivative URL directly with curl to check the actual bytes on the server, independent of browser cache |
image:flush ran, but the derivative still looks low-quality |
The source image itself may genuinely be low-resolution/low-quality (e.g. a blurry auto-selected video thumbnail frame) — flushing only forces regeneration from whatever the current source actually is | Confirm the source file's own real dimensions/quality first (e.g. identify or file on the local file) before assuming the style pipeline is at fault |
Related
- howto-local-dev.md — general DDEV/config workflow
drupal/config/sync/image.style.*.yml— the site's defined image styles