Stop Pre-Generating Image Thumbnails in Laravel — Do It On-The-Fly Instead — CoPilot Blog
    Neura MarketNeura Market/CoPilot
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityCoPilotCoPilot
    DeepSeekDeepSeekStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsBlogVideosGuidesCoursesCommunityPluginsTrendingGenerate
    CoPilotBlogStop Pre-Generating Image Thumbnails in Laravel — Do It On-The-Fly Instead
    Back to Blog
    Stop Pre-Generating Image Thumbnails in Laravel — Do It On-The-Fly Instead
    backend

    Stop Pre-Generating Image Thumbnails in Laravel — Do It On-The-Fly Instead

    Vas May 7, 2026
    0 views

    Every Laravel project eventually hits the same wall: a designer asks for a new image size, and...

    Every Laravel project eventually hits the same wall: a designer asks for a new image size, and suddenly you're writing a migration, a queue job, and a Media Library conversion — just to serve a 400px thumbnail. There's a simpler way. Generate image variants on demand, cache the result permanently, and move on. That's exactly what [laravel-imagepresets](https://github.com/fomvasss/laravel-imagepresets) does. --- ## The Problem With Pre-Generated Thumbnails When you pre-generate image variants (the approach Spatie Media Library encourages by default), you pay upfront: - Storage costs for every variant of every image, even ones never viewed - Queue processing time on upload - Pain when design requirements change and you need to regenerate thousands of files - Complex seeding/migration logic for existing images On-the-fly processing flips this: you process an image **the first time it's requested**, cache the result, and never touch it again. The tradeoff is a slightly slower first request — which is usually invisible to users. --- ## What Is laravel-imagepresets? It's a Laravel package built on [League/Glide](https://glide.thephpleague.com/) that gives you: - A single `/imagepreset` route that handles all image transformations - Automatic caching to any Laravel filesystem disk (local, S3, GCS, FTP) - A clean API — helper function, Facade, and Blade directive - Named presets so you define sizes once and reuse them everywhere - Production-ready security: SSRF protection, allowlists, signed URLs, SVG sanitization --- ## Installation ```bash composer require fomvasss/laravel-imagepresets php artisan vendor:publish --tag=imagepresets-config ``` The service provider is auto-discovered. No manual registration needed. --- ## Your First Image URL ```php // Resize to 800px wide, convert to WebP $url = imagepreset_url('storage/images/photo.jpg', ['w' => 800, 'fm' => 'webp']); // → https://example.com/imagepreset?fm=webp&src=storage%2Fimages%2Fphoto.jpg&w=800 ``` On the first hit, Glide resizes and converts the image, stores it on your configured disk, and returns it with a one-year `Cache-Control` header. The next request? Pure cache — Laravel never runs. --- ## Named Presets: Define Once, Use Everywhere Hardcoding `['w' => 300, 'h' => 200, 'fm' => 'webp', 'fit' => 'crop']` everywhere is a maintenance headache. Named presets solve this. In `config/imagepresets.php`: ```php 'presets' => [ 'thumb' => ['w' => 300, 'h' => 200, 'fm' => 'webp', 'fit' => 'crop'], 'hero' => ['w' => 1200, 'fm' => 'webp', 'q' => 85], 'avatar' => ['w' => 96, 'h' => 96, 'fm' => 'webp', 'fit' => 'crop'], 'og_banner' => ['w' => 1300, 'h' => 650, 'fit' => 'fill-max', 'fm' => 'jpg', 'bg' => 'ffffff'], ], ``` Then in your code: ```php // String shorthand $url = imagepreset_url('photo.jpg', 'thumb'); // Facade Imagepreset::url('photo.jpg', 'hero'); ``` ```blade {{-- Blade directive --}} <img src="@imagepreset('photo.jpg', 'thumb')" alt="Thumbnail"> ``` Need to override one param? Pass it alongside the preset name: ```php // Use thumb preset but output JPG instead of WebP $url = imagepreset_url('photo.jpg', ['preset' => 'thumb', 'fm' => 'jpg']); ``` --- ## Fit Methods Explained The `fit` parameter controls how the image fills the target dimensions. Choosing the wrong one is a common source of stretched or oddly-cropped images. | Fit | Use when... | |-----|-------------| | `crop` | You need exact pixel dimensions (cards, avatars). Edges may be trimmed. | | `contain` | The full image must be visible. No fill — transparent space is left. | | `fill` | Full image visible, remaining canvas filled with `bg` color. May upscale. | | `fill-max` | Like `fill` but never upscales. Great for OG images and social banners. | | `max` | Like `contain` but never upscales beyond original size. | | `stretch` | Forces exact dimensions ignoring aspect ratio. Rarely a good idea. | For OG images, `fill-max` is your friend: ```php $url = imagepreset_url('post-image.jpg', [ 'w' => 1300, 'h' => 650, 'fit' => 'fill-max', 'fm' => 'jpg', 'bg' => 'ffffff', ]); ``` --- ## S3 / Remote Disk Support Just set the disk in your `.env`: ```env IMAGEPRESET_DISK=s3 IMAGEPRESET_PATH=imagepresets ``` The package detects remote disks automatically. Glide processes the image locally, uploads the result to S3 via Flysystem, deletes the local temp file, and streams the response directly from S3. No extra code needed. --- ## Security Out of the Box Open image-resizing endpoints are a common attack surface. The package handles the main threats: **Allowlists** prevent arbitrary dimensions from being requested: ```php 'allowed_widths' => [100, 200, 400, 800, 1200], 'allowed_heights' => [100, 200, 400, 600], 'allowed_formats' => ['webp', 'jpg', 'png'], ``` **SSRF protection** blocks remote image sources that point to private IPs or localhost. **Image bomb protection** rejects files that exceed `max_image_pixels` (default: 150 Mpx). **Signed URLs** (optional) make it impossible to tamper with parameters: ```env IMAGEPRESET_SIGNED_URL=true ``` Once enabled, `imagepreset_url()` automatically generates HMAC-signed URLs. Changing any parameter returns 403. --- ## Race Condition Protection What happens when 50 users simultaneously request the same uncached image? Without protection, you'd process the same image 50 times. The package uses `Cache::lock()` to ensure only one process generates each variant. Set `CACHE_DRIVER=redis` for this to work correctly across multiple servers. --- ## Audit Log: Discover What Your Frontend Actually Needs Before locking down allowlists in production, you can enable audit logging in development to see exactly which params your frontend requests: ```env IMAGEPRESET_AUDIT_LOG=true ``` Then extract unique values from your logs: ```bash grep -oh '"w":[0-9]*' storage/logs/*.log | sort -u ``` Use the findings to populate your allowlists before deploying. --- ## CDN and Nginx Caching Every response ships with: ```http Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable ETag: "<hash>" ``` This makes it trivial to cache at the edge. The README includes ready-to-use configs for Nginx proxy cache and Cloudflare Cache Rules. To verify your cache is working: ```bash # Run twice — first should be MISS, second HIT curl -s -o /dev/null -D - "https://example.com/imagepreset?src=photo.jpg&w=800&fm=webp" ``` --- ## Clearing the Cache ```bash # Clear all cached presets php artisan imagepresets:clear # Clear a specific remote disk php artisan imagepresets:clear --disk=s3 --path=imagepresets ``` --- ## Requirements | Dependency | Version | |---|---| | PHP | ^8.1 | | Laravel | 10 / 11 / 12 / 13 | | league/glide | ^2.0 \| ^3.0 | Optional: `imagick` extension for AVIF output and SVG rasterization. --- ## Wrapping Up If your project doesn't need the full power of Spatie Media Library — associations, conversions, responsive images — and you just want to serve the right image size without pre-generating everything, `laravel-imagepresets` is worth a look. Install it, define a few presets, drop `@imagepreset()` into your Blade templates, and you're done. ```bash composer require fomvasss/laravel-imagepresets ``` → [GitHub](https://github.com/fomvasss/laravel-imagepresets) · [Packagist](https://packagist.org/packages/fomvasss/laravel-imagepresets) --- *Have questions or found a bug? Open an issue on GitHub.*

    Tags

    backendlaravelperformancephp

    Comments

    More Blog

    View all
    Minimalist EKS: The Easy Waykubernetes

    Minimalist EKS: The Easy Way

    Amazon EKS manages the Kubernetes control plane, but you remain responsible for provisioning the...

    J
    Joaquin Menchaca
    Never forget to enter the Stern Grove lottery again!ai

    Never forget to enter the Stern Grove lottery again!

    Browser automation with Playwright, Python, GitHub Actions, and Entire to auto-enter San Francisco Stern Grove concert lotteries each week!

    L
    Lizzie Siegle
    A Free Screenshot Editor That Never Uploads Your Imagetypescript

    A Free Screenshot Editor That Never Uploads Your Image

    A free screenshot and image editor that runs entirely in your browser. Keeping every edit reversible and handling big phone photos, in plain TypeScript and Canvas2D.

    M
    Martin Stark
    I built a CLI to break my highlights out of Apple Booksshowdev

    I built a CLI to break my highlights out of Apple Books

    A macOS CLI + MCP server that exports Apple Books highlights to Markdown and gives AI assistants direct access to your reading notes.

    A
    Andrey Korchak
    A Developer's Guide to Agent Hooks in Antigravity CLIai

    A Developer's Guide to Agent Hooks in Antigravity CLI

    Motivation To be quite honest, "Hooks"—the shell commands we trigger at specific points...

    T
    Tanaike
    Tactical vs. Strategic Agentic AI Development — A Playbook for Developersagents

    Tactical vs. Strategic Agentic AI Development — A Playbook for Developers

    The Strategic Engineer: Why Writing Code Is No Longer Your Most Valuable Skill ...

    A
    Adewumi Saheed Adewale

    Stay up to date

    Get the latest CoPilot prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for CoPilot and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.