Problem/Motivation

GSMI's cached derivative images are keyed by the node's changed timestamp, so editing a node makes future page renders resolve a brand-new image URL, and GsmiImageManager::invalidateSocialImage() deletes the old cached file on hook_entity_update().

That's correct for anyone who re-fetches the page — they get the new URL. But external consumers that cache the image URL itself independently of the page (CDN edge caches, social-media crawlers like Facebook's/Twitter's og:image fetchers, browsers with a cached response) can still hold a reference to the old URL. Once the old file is deleted, a request to that URL simply 404s, with no way to recover the current image.

Proposed resolution

Add a fallback route that catches requests for a derivative that no longer exists on disk and redirects (302, Cache-Control: no-store) to the node's current derivative instead of returning a 404.

This required a bigger change than just adding a distinguishing path segment: GSMI's cache previously lived nested inside the image style's own derivative directory ({scheme}://styles/{style}/{scheme}/{nid}/{field}/{timestamp}/...). Core's image module registers an inbound path processor (PathProcessorImageStyles) that unconditionally intercepts any request under {scheme}://styles/... before routing runs, so a custom GSMI route nested in that same tree would never actually be reached — core's own image-style regeneration logic would claim the request first (and fail, since the GSMI-specific path segments aren't a valid source image path).

The fix moves GSMI's cache to its own sibling tree, {scheme}://gsmi/{style}/{nid}/{field}/{timestamp}/{filename}, completely outside the styles/ prefix core claims. A new route_callbacks class (GsmiStaleImageRoutes, mirroring core's own Drupal\image\Routing\ImageStyleRoutes) registers a route matching that path, backed by a new GsmiController::redirectStaleDerivative() method that reloads the node, resolves (regenerating if needed) its current derivative, and redirects to it.

Changes

  • GsmiGenerator: cache path moved out from under the image style's own styles/ directory into {scheme}://gsmi/{style}/....
  • New GsmiStaleImageRoutes + GsmiController::redirectStaleDerivative() for the fallback redirect.
  • Updated/added unit and kernel test coverage for the new path structure and the redirect controller.
  • README.md/AGENTS.md updated to document the new cache layout and stale-URL fallback behavior.

Remaining limitations

This only helps requests that reach the origin again (a CDN's own TTL expiring, a crawler re-fetching, a browser reload) — it can't retroactively fix bytes a client already cached locally without re-requesting.

Issue fork gsmi-3610139

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mandclu created an issue. See original summary.

  • mandclu committed 1b4c89b9 on 2.0.x
    feat: #3610139 Redirect requests for stale/deleted derivative images...
mandclu’s picture

Issue summary: View changes
Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.