Skip to content

[6.x] Hybrid Glide image caching - #14570

Draft
duncanmcclean wants to merge 18 commits into
6.xfrom
glide-half-measure-caching
Draft

duncanmcclean wants to merge 18 commits into
6.xfrom
glide-half-measure-caching

Conversation

@duncanmcclean

@duncanmcclean duncanmcclean commented Apr 29, 2026

Copy link
Copy Markdown
Member

This pull request implements a new "hybrid" caching strategy for Glide image manipulation.

The Problem

Statamic currently offers two Glide caching modes:

  • Dynamic (cache: false) — Fast template rendering, but every image request hits PHP, even after the image has already been generated.
  • Static (cache: true) — Images are served directly by the web server, but they're all eagerly generated during template rendering. A page with 10 images × 11 variants = 110 images blocking the page from even loading.

Hybrid Caching

Setting cache to 'hybrid' enables a new mode that combines the best of both:

// config/statamic/assets.php
'image_manipulation' => [
    'cache' => 'hybrid',
    'cache_path' => public_path('img'),
],

The Glide tag outputs a URL pointing to where the cached image will live, but doesn't generate the image during template rendering. When the browser requests the image:

  1. If the file already exists on disk, the web server (Nginx/Apache) serves it directly — no PHP needed.
  2. If the file doesn't exist yet, the request falls through to PHP, which generates the image, saves it to the public cache_path, and serves it.

After the first request, the web server serves the static file directly on all subsequent requests. The default Nginx/Apache/IIS configurations handle this automatically — no additional rewrite rules are needed.

How It Works

  • A new GlideCachePathResolver predicts the deterministic cache path for an image without generating it.
  • A new HybridUrlBuilder builds clean URLs using the cache path, and stores a mapping in the Glide cache store so the controller knows how to generate the image later.
  • The GlideController handles the PHP fallback: it checks if the file already exists (logging a debug message if server rewrite rules aren't configured), looks up the mapping, and generates the image on demand.
  • Cache invalidation and php please glide:clear work automatically — the cache disk is the public path, so existing cleanup logic handles it.

Closes statamic/ideas#1420

Docs PR: statamic/docs#1899

@duncanmcclean duncanmcclean changed the title [6.x] Half-measure Glide image caching [6.x] Hybrid Glide image caching Apr 29, 2026
@daun

daun commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

🔥

@jasonvarga jasonvarga left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hybrid approach looks great overall — one bug to address before merging (inline). A few smaller notes I'll leave as a follow-up comment so they don't block.

Comment thread src/Imaging/HybridUrlBuilder.php Outdated
duncanmcclean and others added 2 commits May 14, 2026 09:01
When `itemType()` returns 'id', the item is already a valid asset ID
like `container::folder/file.jpg`. The str_replace was incorrectly
converting `/` to `::`, producing invalid IDs like
`container::folder::file.jpg`.

This caused `Assets::find()` to return null, leading to fatal errors
when building hybrid URLs via `Image::manipulate('container::path')`.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@duncanmcclean
duncanmcclean requested a review from jasonvarga May 14, 2026 08:05
@jasonvarga
jasonvarga marked this pull request as draft June 4, 2026 20:52
@jasonvarga

Copy link
Copy Markdown
Member

Did a bit of a more thorough review and this introduces a security issue in its current state. I've marked it as draft so we don't merge prematurely.

@jasonvarga
jasonvarga dismissed their stale review June 4, 2026 20:54

Changes were made but there's more to do.

@aaronbushnell

Copy link
Copy Markdown
Contributor

Ooo really looking forward to this one! 🔥

…hing

# Conflicts:
#	src/Imaging/ImageGenerator.php

@jasonvarga jasonvarga left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hybrid design itself is solid — the clean URL is only generatable from a mapping the server wrote during rendering, so the mass-resize vector is closed for hybrid's own URLs. I checked this out and exercised it; the inline comments are the things that need fixing before this can merge. One is a page-breaking crash, the rest are correctness/security gaps in the surrounding routes.

Non-blocking, but worth doing while you're in here:

  • generateOnDemand() passes the raw request path to Glide::cacheDisk()->exists(). Paths like /img/../../.env throw League\Flysystem\PathTraversalDetected (correctly blocked, no disclosure) but surface as a 500 rather than a 404. Catch it and 404.
  • Direct serving only works when route matches the public-relative cache_path (img / public_path('img')), and nothing checks that. If someone changes one, every image request silently hits PHP forever. Worth a docs note or a boot-time warning.
  • A published hybrid URL is only resolvable while its mapping survives in the Glide cache store. On a statically cached site, losing storage/framework/cache/glide (e.g. a container redeploy) while the HTML is retained means those images 404 permanently. Docs note at minimum.
  • resolveForUrl() and the 'url' mapping branch have no tests.

Comment thread src/Imaging/HybridUrlBuilder.php Outdated
Comment thread routes/routes.php
Comment thread src/Imaging/HybridUrlBuilder.php Outdated
Comment thread src/Imaging/GlideCachePathResolver.php
Comment thread tests/Imaging/GlideTest.php Outdated
duncanmcclean and others added 9 commits September 14, 2026 20:34
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U772LC9aHMR8DvJ76tBDYt
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U772LC9aHMR8DvJ76tBDYt
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U772LC9aHMR8DvJ76tBDYt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hybrid approach to Asset caching for massive performance benefits

4 participants