coderello/laravel-proximage
Proximage lets you resize, crop, and manipulate images on the fly by simply tweaking their URLs—no pre-generated thumbnails needed. Includes caching, signed URLs, and placeholder support for fast, flexible image handling in Laravel.
Install the package:
composer require coderello/laravel-proximage
Publish the config (the package ships a proximage.php config file that controls the route, secret, and image constraints):
php artisan vendor:publish --provider="Coderello\Proximage\ProximageServiceProvider"
Set the secret in .env so the proxy endpoint can't be abused by third parties:
PROXIMAGE_SECRET=some-long-random-string
The package auto-registers a proxy route (default /proximage). Confirm it's there before writing any views:
php artisan route:list | grep proximage
Where to look first: open config/proximage.php and read the package's routes/ and src/ directories. That's where the allowed query parameters, the signing logic, and the cache/storage behaviour live. Everything else about the package follows from those two places.
First use case: you have a remote image URL (Unsplash, S3, a legacy CMS) and want to serve a resized, cached version without touching your own storage. In a Blade view:
<img src="{{ proximage('https://images.unsplash.com/photo-1234')->width(400)->height(300) }}">
The helper returns an object that stringifies to a signed URL pointing at the proxy route. The route fetches the source, applies the transformations, and streams the result back.
Wrap the helper, don't scatter it. Define your own presenter/helper layer so sizes, quality, and fallbacks are centralised:
// app/Support/Image.php
class Image
{
public static function thumb(string $url): string
{
return (string) proximage($url)->width(200)->height(200);
}
public static function hero(string $url): string
{
return (string) proximage($url)->width(1600)->height(900);
}
}
Then views call Image::thumb($post->cover). When you eventually swap the package for a CDN, only one file changes.
Responsive srcset. Proximage is at its best when it fronts a single origin URL for many sizes:
<img
src="{{ proximage($url)->width(800) }}"
srcset="{{ proximage($url)->width(400) }} 400w,
{{ proximage($url)->width(800) }} 800w,
{{ proximage($url)->width(1600) }} 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="">
Because the proxy route caches per unique query string, the second and third requests for a given size are cheap.
Cache in layers. Browser/proxy caching is handled by response headers; the package also stores processed images on disk. Put a CDN in front of the route and make sure the CDN is configured to include the query string in the cache key, otherwise every image will collide.
Treat the source URL as untrusted input. Never pass a raw request('url') into proximage(). Validate against an allowlist of hosts (your own bucket, a known CDN) before generating the link, and keep PROXIMAGE_SECRET out of the client. The signature is what stops a stranger from using your app as a free image-resizing service.
Local development. Point the config at a permissive secret in .env.testing / .env.local and use the package's route to sanity-check transformations before wiring them into Blade:
http://your-app.test/proximage?url=...&width=300&height=300&signature=...
Copy the exact URL the helper generates rather than hand-building query strings — signature mismatches are the most common failure.
Migration path. Keep the helper return type cast to string in your code and store only the source URL in the database. Never persist a Proximage URL in a column — if the secret rotates or the route prefix changes, every stored row becomes stale.
Maintenance risk. Last release was November 2020 (Laravel 8 era). Before adopting, verify it installs on your Laravel/PHP version, and especially check whether it pulls Intervention Image v2, which conflicts with v3 projects. If you're on Laravel 10/11, you may need intervention/image pinned or a fork.
SSRF and open-proxy abuse. If the secret is empty or validation is skipped, the route will fetch any URL the server can reach, including http://169.254.169.254/ (cloud metadata) and internal hosts. Always set a secret in production and consider adding a host allowlist in your own middleware.
Memory and timeouts on large sources. A 6000×4000 JPEG can blow past memory_limit or PHP's max_execution_time in GD/Imagick. Cap max_width/max_height in the config, and prefer source images that are already reasonably sized.
GD vs Imagick. Confirm which driver is installed (php -m | grep -E 'gd|imagick') and that your driver supports the transformations you use. Some filters behave slightly differently between drivers.
Storage permissions. The package writes processed files under storage/. On a fresh deploy, php artisan storage:link and correct storage/ ownership are prerequisites, not optional steps. Cache-clear after changing the config:
php artisan config:clear
Config caching. php artisan config:cache in production means any change to config/proximage.php requires re-caching. A rotated secret without re-caching is a silent stream of 403s.
Debugging flow: route:list → check the generated URL includes a signature → hit the URL directly in the browser → check storage/logs/laravel.log → check the proxy cache directory. Most "it's broken" reports are (a) a signature mismatch from a tampered query string, or (b) a source URL returning a 4xx/5xx that the package surfaces only in the logs.
Extension points. The service provider is small, so if the package doesn't expose what you need, overriding the config, swapping the route middleware, or writing a thin controller of your own that calls the underlying handler is usually cleaner than forking.
Ask whether you need it. For a handful of sizes on a low-traffic site, intervention/image called once at upload time and stored on disk or S3 is simpler and has no runtime proxy in the request path. Proximage earns its place when the source images are remote, the size matrix is large, and you want on-demand generation with a cache.
How can I help you explore Laravel packages today?