imsus / laravel-imgproxy
Laravel integration for ImgProxy
Fund package maintenance!
Imam Susanto
Requires
- php: ^8.2
- illuminate/contracts: ^10.0|^11.0|^12.0
- spatie/laravel-package-tools: ^1.19
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^v1.21
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^2.34
- pestphp/pest-plugin-arch: ^2.8
- pestphp/pest-plugin-laravel: ^2.3
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.3
README
A comprehensive Laravel package for ImgProxy integration. Generate optimized, signed image URLs with fluent API including resizing, quality control, visual effects, and advanced processing options.
Features
- ๐ Fluent API - Clean, chainable method syntax
- ๐ Secure URLs - HMAC-SHA256 signed URLs with hex key/salt validation
- ๐จ Visual Effects - Blur, sharpen, brightness, contrast, saturation adjustments
- โก Quality Control - Fine-tune compression for JPEG, WebP, AVIF formats
- ๐ Flexible Resizing - Multiple resize modes with DPR support
- ๐ง Laravel Integration - Service provider, facade, and helper function
- โ Type Safe - PHP 8.2+ enums and comprehensive validation
- ๐งช Well Tested - 39+ tests with workbench integration & visual testing
Installation
You can install the package via composer:
composer require imsus/laravel-imgproxy
You can publish the config file with:
php artisan vendor:publish --tag="imgproxy-config"
This is the contents of the published config file:
return [ 'endpoint' => env('IMGPROXY_ENDPOINT', 'http://localhost:8080'), 'key' => env('IMGPROXY_KEY'), 'salt' => env('IMGPROXY_SALT'), 'default_source_url_mode' => env('IMGPROXY_DEFAULT_SOURCE_URL_MODE', 'encoded'), 'default_output_extension' => env('IMGPROXY_DEFAULT_OUTPUT_EXTENSION', 'jpeg'), ];
Configuration
You can configure the package by updating the values in your .env
file:
IMGPROXY_ENDPOINT=http://localhost:8080 IMGPROXY_KEY=your_hex_key_here IMGPROXY_SALT=your_hex_salt_here IMGPROXY_DEFAULT_SOURCE_URL_MODE=encoded IMGPROXY_DEFAULT_OUTPUT_EXTENSION=jpeg
Note
The key
and salt
are required only if you want to generate signed URLs. If you don't want to generate signed URLs, you can leave them empty.
Caution
The key
and salt
should be in hex-encoded format. Generate them using: openssl rand -hex 32
Configuration Options
Option | Description | Default | Options |
---|---|---|---|
endpoint |
ImgProxy server URL | http://localhost:8080 |
Any valid URL |
key |
Hex-encoded signing key | null |
64-char hex string |
salt |
Hex-encoded signing salt | null |
64-char hex string |
default_source_url_mode |
How to encode source URLs | encoded |
encoded , plain |
default_output_extension |
Default output format | jpeg |
jpeg , png , webp , avif , etc. |
Usage
Basic Usage
use Imsus\ImgProxy\Facades\ImgProxy; use Imsus\ImgProxy\Enums\OutputExtension; use Imsus\ImgProxy\Enums\ResizeType; // Generate URL using Facade $url = ImgProxy::url('https://example.com/image.jpg') ->setWidth(300) ->setHeight(200) ->build(); // Generate URL using helper function $url = imgproxy('https://example.com/image.jpg') ->setWidth(300) ->setHeight(200) ->build();
Resizing & Formatting
$url = imgproxy('https://example.com/image.jpg') ->setWidth(400) ->setHeight(300) ->setResizeType(ResizeType::FILL) ->setExtension(OutputExtension::WEBP) ->setDpr(2) // High DPI displays ->build();
Quality Control
// Optimize for different use cases $thumbnail = imgproxy($image) ->setWidth(150) ->setHeight(150) ->setQuality(70) // Lower quality for thumbnails ->build(); $hero = imgproxy($image) ->setWidth(1200) ->setHeight(600) ->setQuality(90) // Higher quality for hero images ->build();
Visual Effects
$url = imgproxy('https://example.com/photo.jpg') ->setWidth(500) ->setHeight(300) ->setBlur(2.0) // Blur effect ->setSharpen(1.5) // Sharpen details ->setBrightness(20) // Increase brightness ->setContrast(1.2) // Enhance contrast ->setSaturation(1.1) // Boost saturation ->build();
Advanced Processing
use Imsus\ImgProxy\Enums\SourceUrlMode; // Plain URL mode for debugging $url = imgproxy('https://example.com/image.jpg') ->setMode(SourceUrlMode::PLAIN) ->setWidth(300) ->setHeight(200) ->build(); // Custom processing options $url = imgproxy('https://example.com/image.jpg') ->setProcessing('rs:fill:400:300:1/rt:fit/q:85/bl:2.0') ->build();
Method Chaining Examples
// Complete image optimization pipeline $optimizedUrl = imgproxy($originalImage) ->setWidth(800) ->setHeight(600) ->setResizeType(ResizeType::FILL) ->setExtension(OutputExtension::WEBP) ->setQuality(85) ->setSharpen(1.0) ->setDpr(2) ->build(); // Portrait enhancement $portraitUrl = imgproxy($portrait) ->setWidth(400) ->setHeight(600) ->setResizeType(ResizeType::FILL) ->setBrightness(10) ->setContrast(1.1) ->setSaturation(1.05) ->setQuality(90) ->build();
API Reference
Available Methods
Method | Parameters | Description |
---|---|---|
url(string $url) |
Image URL | Set the source image URL |
setWidth(int $width) |
Width in pixels | Set image width |
setHeight(int $height) |
Height in pixels | Set image height |
setResizeType(ResizeType $type) |
Resize mode | Set how image should be resized |
setExtension(OutputExtension $ext) |
Output format | Set output image format |
setDpr(int $dpr) |
1-8 | Set device pixel ratio |
setQuality(int $quality) |
0-100 | Set compression quality |
setBlur(float $sigma) |
โฅ0.0 | Apply blur effect |
setSharpen(float $sigma) |
โฅ0.0 | Apply sharpen effect |
setBrightness(int $brightness) |
-255 to 255 | Adjust brightness |
setContrast(float $contrast) |
โฅ0.0 | Adjust contrast |
setSaturation(float $saturation) |
โฅ0.0 | Adjust saturation |
setMode(SourceUrlMode $mode) |
encoded /plain |
Set URL encoding mode |
setProcessing(string $options) |
Processing string | Custom processing options |
build() |
- | Generate final URL |
Enums
ResizeType
ResizeType::FIT
- Resize keeping aspect ratio to fit dimensionsResizeType::FILL
- Resize keeping aspect ratio to fill dimensions (crops overflow)ResizeType::FILL_DOWN
- Same as fill, but maintains requested aspect ratio for smaller imagesResizeType::FORCE
- Resize without keeping aspect ratioResizeType::AUTO
- Automatically choose between fit/fill based on orientation
OutputExtension
OutputExtension::JPEG
- JPEG formatOutputExtension::PNG
- PNG formatOutputExtension::WEBP
- WebP formatOutputExtension::AVIF
- AVIF formatOutputExtension::GIF
- GIF formatOutputExtension::ICO
- ICO formatOutputExtension::SVG
- SVG formatOutputExtension::HEIC
- HEIC formatOutputExtension::BMP
- BMP formatOutputExtension::TIFF
- TIFF format
SourceUrlMode
SourceUrlMode::ENCODED
- Base64 encode source URL (default)SourceUrlMode::PLAIN
- Use plain text URL
Error Handling
The package includes comprehensive validation and will throw InvalidArgumentException
for invalid parameters:
try { $url = imgproxy('invalid-url') ->setQuality(150) // Invalid: > 100 ->build(); } catch (InvalidArgumentException $e) { // Handle validation error echo $e->getMessage(); // "Quality must be between 0 and 100" }
For invalid URLs, the package gracefully returns the original URL instead of throwing an exception.
Troubleshooting
Common Issues
Problem: Getting "insecure" URLs instead of signed URLs
http://localhost:8080/insecure/width:300/height:200/...
Solution: Ensure IMGPROXY_KEY
and IMGPROXY_SALT
are set in your .env
file with valid hex values.
Problem: Invalid hex key/salt errors
InvalidArgumentException: The key must be a hex-encoded string.
Solution: Generate proper hex keys:
# Generate 32-byte hex key and salt
openssl rand -hex 32
Problem: Images not loading/404 errors Solutions:
- Verify ImgProxy server is running at the configured endpoint
- Check source image URLs are accessible
- Ensure ImgProxy server can reach source URLs (firewall/network issues)
Problem: Poor image quality
Solutions:
- Increase quality setting:
->setQuality(90)
- Use appropriate output format:
->setExtension(OutputExtension::WEBP)
- Avoid excessive sharpening:
->setSharpen(1.0)
instead of higher values
Debug Mode
Enable plain URL mode for debugging:
$debugUrl = imgproxy('https://example.com/image.jpg') ->setMode(SourceUrlMode::PLAIN) ->setWidth(300) ->build(); echo $debugUrl; // Output: http://localhost:8080/signature/width:300/plain/https://example.com/image.jpg@jpg
Advanced Usage & Patterns
Performance Optimization
Choose optimal quality settings based on image use case:
// Thumbnails - prioritize small file size $thumbnail = imgproxy($image) ->setWidth(150) ->setHeight(150) ->setQuality(60) ->setExtension(OutputExtension::WEBP) ->build(); // Hero images - balance quality and size $hero = imgproxy($image) ->setWidth(1920) ->setHeight(1080) ->setQuality(85) ->setExtension(OutputExtension::WEBP) ->build(); // Product images - prioritize quality $product = imgproxy($image) ->setWidth(800) ->setHeight(600) ->setQuality(95) ->setSharpen(0.5) ->build();
Format Selection Strategy
// Modern browsers - use AVIF for best compression $avifUrl = imgproxy($image) ->setExtension(OutputExtension::AVIF) ->setQuality(75) // AVIF allows lower quality with better visual results ->build(); // Fallback for older browsers - use WebP $webpUrl = imgproxy($image) ->setExtension(OutputExtension::WEBP) ->setQuality(85) ->build(); // Universal fallback - use JPEG $jpegUrl = imgproxy($image) ->setExtension(OutputExtension::JPEG) ->setQuality(90) ->build();
Laravel Integration
Blade Directives
Create custom Blade directives for common use cases:
// In AppServiceProvider::boot() use Illuminate\Support\Facades\Blade; Blade::directive('imgproxy', function ($expression) { return "<?php echo imgproxy($expression)->build(); ?>"; }); Blade::directive('avatar', function ($expression) { return "<?php echo imgproxy($expression)->setWidth(150)->setHeight(150)->setResizeType(\Imsus\ImgProxy\Enums\ResizeType::FILL)->build(); ?>"; });
{{-- Usage in Blade templates --}} <img src="@imgproxy($product->image)" alt="Product"> <img src="@avatar($user->avatar)" alt="User Avatar">
Eloquent Accessors
Add image processing to Eloquent models:
class User extends Model { public function getAvatarUrlAttribute(): string { if (!$this->avatar) { return '/default-avatar.png'; } return imgproxy($this->avatar) ->setWidth(150) ->setHeight(150) ->setResizeType(ResizeType::FILL) ->setExtension(OutputExtension::WEBP) ->setQuality(85) ->build(); } }
API Resources
Use in API resources for consistent image URLs:
class UserResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'avatar' => [ 'small' => imgproxy($this->avatar)->setWidth(50)->setHeight(50)->build(), 'medium' => imgproxy($this->avatar)->setWidth(150)->setHeight(150)->build(), 'large' => imgproxy($this->avatar)->setWidth(300)->setHeight(300)->build(), ], ]; } }
Common Patterns
Avatar Processing
class UserAvatar { public static function generate(string $imageUrl, int $size = 150): string { return imgproxy($imageUrl) ->setWidth($size) ->setHeight($size) ->setResizeType(ResizeType::FILL) ->setExtension(OutputExtension::WEBP) ->setQuality(85) ->setSharpen(0.5) ->build(); } } // Usage $avatarUrl = UserAvatar::generate($user->profile_image, 200);
Responsive Images
Generate multiple image sizes for responsive images:
class ResponsiveImage { public static function generateSrcset(string $imageUrl, array $sizes): array { $srcset = []; foreach ($sizes as $width) { $url = imgproxy($imageUrl) ->setWidth($width) ->setHeight(intval($width * 0.75)) // 4:3 aspect ratio ->setResizeType(ResizeType::FILL) ->setExtension(OutputExtension::WEBP) ->setQuality(85) ->build(); $srcset[] = "{$url} {$width}w"; } return $srcset; } } // Usage $sizes = [400, 800, 1200, 1600]; $srcset = ResponsiveImage::generateSrcset($image, $sizes); $srcsetString = implode(', ', $srcset);
<img src="{{ imgproxy($image)->setWidth(800)->build() }}" srcset="{{ $srcsetString }}" sizes="(max-width: 768px) 100vw, 50vw" alt="Responsive image">
Image Processing Recipes
Photo Enhancement
// Portrait enhancement $enhancedPortrait = imgproxy($portrait) ->setWidth(600) ->setHeight(800) ->setResizeType(ResizeType::FILL) ->setBrightness(8) // Slightly brighter ->setContrast(1.1) // Enhanced contrast ->setSaturation(1.05) // Subtle saturation boost ->setSharpen(0.8) // Gentle sharpening ->setQuality(92) ->build(); // High contrast black and white $highContrastBW = imgproxy($image) ->setWidth(800) ->setHeight(600) ->setSaturation(0) // Remove all color ->setContrast(1.5) // High contrast ->setBrightness(-5) // Slightly darker ->setSharpen(2.0) // Sharp details ->build();
E-commerce Optimization
// Clean product photos $productClean = imgproxy($product) ->setWidth(800) ->setHeight(800) ->setResizeType(ResizeType::FIT) ->setBrightness(15) // Bright and clean ->setContrast(1.1) // Good contrast ->setSharpen(1.5) // Sharp product details ->setQuality(95) // High quality for products ->setExtension(OutputExtension::WEBP) ->build();
Security Best Practices
Environment Configuration
# Use strong, unique keys IMGPROXY_KEY=$(openssl rand -hex 32) IMGPROXY_SALT=$(openssl rand -hex 32) # Use HTTPS in production IMGPROXY_ENDPOINT=https://imgproxy.yoursite.com # Consider using encoded mode for security IMGPROXY_DEFAULT_SOURCE_URL_MODE=encoded
URL Validation
Always validate source URLs before processing:
class ImageProcessor { private array $allowedDomains = [ 'your-cdn.com', 'storage.googleapis.com', 's3.amazonaws.com', ]; public function processImage(string $imageUrl): string { $parsedUrl = parse_url($imageUrl); if (!in_array($parsedUrl['host'], $this->allowedDomains)) { throw new InvalidArgumentException('Image domain not allowed'); } return imgproxy($imageUrl) ->setWidth(800) ->setHeight(600) ->setQuality(85) ->build(); } }
Testing
Unit & Integration Tests
# Run all tests composer test # Run only unit tests composer test --filter=ImgProxyTest # Run only integration tests composer test --filter=WorkbenchIntegrationTest # Run with coverage composer test-coverage
Interactive Testing with Workbench
The package includes a comprehensive workbench environment for interactive testing:
# Build and start the workbench server composer start # Or build separately and serve composer build php vendor/bin/testbench serve
Once the server is running (typically at http://localhost:8000
), you can access:
API Test Endpoints
- Test Overview:
http://localhost:8000/imgproxy-test/
- JSON overview of all available tests - Basic Test:
http://localhost:8000/imgproxy-test/basic
- Basic URL generation testing - Effects Test:
http://localhost:8000/imgproxy-test/effects
- Quality and visual effects testing - Formats Test:
http://localhost:8000/imgproxy-test/formats
- Format conversion (JPEG, PNG, WebP, AVIF) - Resize Test:
http://localhost:8000/imgproxy-test/resize
- Different resize types comparison - Facade vs Helper:
http://localhost:8000/imgproxy-test/facade-vs-helper
- Compare facade and helper output - Config Test:
http://localhost:8000/imgproxy-test/config
- Configuration validation - Error Handling:
http://localhost:8000/imgproxy-test/error-handling
- Error scenarios testing - Performance Test:
http://localhost:8000/imgproxy-test/performance
- Performance benchmarks
Visual Testing
- Visual Test Suite:
http://localhost:8000/imgproxy-visual-test
- Complete browser-based visual testing
The visual test page includes:
- Real Image Processing - See actual ImgProxy results with sample images
- Quality Comparison - Side-by-side quality levels (30%, 70%, 95%)
- Format Comparison - Visual differences between JPEG, PNG, WebP, AVIF
- Resize Types Demo - Visual behavior of fit, fill, force, auto modes
- Effects Showcase - Blur, sharpen, saturation, brightness effects
- Complex Processing - Portrait enhancement and vintage effects
- High DPI Examples - Standard vs 2x DPI comparisons
Test Coverage
The package includes 39 comprehensive tests with 145 assertions covering:
- โ Unit Tests (26 tests) - Core functionality, validation, edge cases
- โ Integration Tests (13 tests) - Laravel environment, HTTP endpoints, service provider registration
- โ Architecture Tests (7 tests) - Code structure, security, conventions
- โ Visual Tests - Browser-based real image processing validation
- โ Performance Tests - URL generation speed benchmarks (>1000 URLs/second)
Sample Test Responses
Basic Test Response:
{ "original": "https://picsum.photos/800/600", "processed": "http://localhost:8080/signed-url/width:400/height:300/...", "test": "basic_url_generation" }
Performance Test Response:
{ "urls_generated": 100, "duration_seconds": 0.0089, "urls_per_second": 1123.6, "test": "performance" }
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.