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.
- 🚀 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
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'),
];
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
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. |
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();
$url = imgproxy('https://example.com/image.jpg')
->setWidth(400)
->setHeight(300)
->setResizeType(ResizeType::FILL)
->setExtension(OutputExtension::WEBP)
->setDpr(2) // High DPI displays
->build();
// 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();
$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();
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();
// 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();
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 |
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::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::ENCODED
- Base64 encode source URL (default)SourceUrlMode::PLAIN
- Use plain text URL
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.
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
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
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();
// 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();
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">
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();
}
}
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(),
],
];
}
}
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);
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">
// 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();
// 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();
# 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
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();
}
}
# 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
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:
- 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 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
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)
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"
}
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.