axn/laravel-glide
Integrate Glide image manipulation into Laravel with multiple server support and signed, secure URLs. Install via Composer, publish the config, generate a signing key, and serve resized images easily.
Install and bootstrap (≈10 minutes):
composer require axn/laravel-glide
php artisan vendor:publish --tag="glide-config"
php artisan glide:key-generate
glide:key-generate writes GLIDE_SIGN_KEY into .env (v4 fixed a bug where it reported success without writing when the line was missing — check the file after running on any 3.x install). Commit nothing secret; treat the key like APP_KEY.
Where to look first, in order:
config/glide.php — the whole mental model lives here. You get an array of named servers, each with source disk, cache disk, optional watermarks disk, driver (gd/imagick), base_url, defaults, and presets.Axn\LaravelGlide\Facades\Glide, relocated/renamed in 3.0) — read its @method annotations; they are the real API reference.docs/ — the full documentation and it is in French. Budget translation time on onboarding, or have one dev write an internal English cheat-sheet covering url(), imageResponse(), imageAsBase64(), and presets.UPGRADE.md before any major bump — 3.0.0 and 4.0.0 both contain breaking changes.First vertical slice — one server, one preset, one Blade usage:
// config/glide.php
'servers' => [
'default' => [
'source' => 'public',
'cache' => 'public',
'driver' => 'gd',
'presets' => [
'thumb' => ['w' => 320, 'h' => 240, 'fit' => 'crop'],
'hero' => ['w' => 1600, 'fm' => 'webp', 'q' => 80],
],
],
],
<img src="{{ Glide::url('uploads/photo.jpg', ['p' => 'thumb']) }}" alt="">
Serve it, confirm the derivative lands on your cache disk, then confirm storage:link exposes that path if you're caching to a public local disk. Once that URL renders you've validated the four things that actually break: GD/Imagick present, source disk readable, cache disk writable, signing key set.
Presets are the primary abstraction — build the catalogue before the code. The single biggest day-to-day win over raw league/glide is that ['p' => 'thumb'] replaces scattered ['w' => 320, 'h' => 240, 'fit' => 'crop'] arrays. In practice: sit with design, enumerate every variant the product needs (avatar, thumb, card, hero, og-image, zoom), name them once, and forbid raw transformation arrays in application code. Enforce that in review — it's what keeps crops consistent across web, API, and email.
Views and controllers. Views call Glide::url(path, params) and emit a string. Controllers that must generate and return the bytes use Glide::imageResponse(path, params) (implements ResponseFactoryInterface since 4.0) or Glide::imageAsBase64() for API payloads. Keep the base64 path for small icons only; it inflates JSON payloads and defeats cache headers.
Multi-server is per-surface, not per-request. Define one server per storage policy, not per feature: products (S3 source, CDN base_url), avatars (local, aggressive square crop defaults), private (signed, non-public cache). Then reach into them explicitly:
Glide::server('avatars')->url($user->avatar_path, ['p' => 'sm']);
The ServerManager is aliased in the container in 4.0, so you can type-hint it instead of using the facade when you want the dependency explicit — worth doing inside your own wrapper (below).
Wrap the facade behind your own adapter. Given adoption risk (10 stars, 0 dependents, single-agency maintainer), introduce something like App\Image\ImageUrlGenerator with thumb(), hero(), avatar() methods that internally call Glide::server(...)->url(...). This costs an hour and gives you a one-file swap to direct league/glide or a SaaS CDN later. It also gives you a place to add logging, fallbacks, and a default-preset guard.
Route registration per surface. 4.0 added Glide::routes() and a generic invokable GlideController, registering routes named glide.{server} so they sit inside your own middleware group:
Route::middleware(['web', 'auth'])->group(function () {
Glide::routes('private');
});
This is the pattern for gated media: the route lives under your auth middleware, and the signed URL still guards tampering. Don't skip the signature just because the route is authenticated — signed URLs let you hand a URL to a browser/CDN cache without a session.
Cache operations. php artisan glide:clear clears one server's cache or all of them. Wire it into your deploy or content-publish pipeline when source images are replaced in place under the same path — otherwise stale derivatives persist. Prefer immutable filenames (content hashes) over clearing caches as your primary strategy.
Testing. The package ships PHPUnit + Orchestra Testbench as of 4.0, so you can assert on generated URLs without hitting the filesystem. In your feature tests, assert on the preset name in the URL rather than pixel output; assert one full imageResponse() smoke test per driver in an integration suite so a missing GD/Imagick extension fails CI rather than production.
"axn/laravel-glide": "^4.0"), not *.4.0.0 on Packagist was mis-tagged (it pointed at 3.1.1 code). 4.0.1 is the republished fix. Don't pin 4.0.0.sign_key per server with no documented rotation window. If you rotate, all previously generated URLs 404 immediately — including ones in caches, emails, and CDN edge nodes. Plan rotation as a deploy-time cutover, or add a secondary accepted key in your own middleware before rotating.GLIDE_SIGN_KEY matches the environment that generated the URL, server name spelled correctly.InvalidArgumentException (fixed from ErrorException in 4.0). That's a hard fail at runtime, not a silent fallback to default. Watch for typos in Glide::server('avtar') reaching production.php -m | grep -E 'gd|imagick' to your container healthcheck.public cache disk needs storage:link; an S3 cache disk needs base_url pointed at a CDN or the URLs are region-bound and slow. New in 4.0, per-server group_cache_in_folders, cache_with_file_extensions, and temp_dir control path layout and scratch space — set temp_dir explicitly in containerised environments where sys_get_temp_dir() is read-only or tiny.config/glide.php against the docs. Pick one convention and stick to it: presets only, or preset + additive overrides. Mixing both without knowing which wins produces "why is this image 1600px wide" bugs that are painful to trace.league/glide. Reimplementing at parity is on the order of a few engineer-days. If you hit a limitation — multi-key signing, a custom cache key strategy, per-tenant key isolation — forking this is more sensible than patching around it from application code. Keep the wrapper from the Implementation Patterns section so that fork stays surgical.How can I help you explore Laravel packages today?