imsus/laravel-imgproxy

Laravel integration for ImgProxy

v0.4.0 2025-07-18 05:57 UTC

This package is auto-updated.

Last update: 2025-08-18 01:40:00 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

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 dimensions
  • ResizeType::FILL - Resize keeping aspect ratio to fill dimensions (crops overflow)
  • ResizeType::FILL_DOWN - Same as fill, but maintains requested aspect ratio for smaller images
  • ResizeType::FORCE - Resize without keeping aspect ratio
  • ResizeType::AUTO - Automatically choose between fit/fill based on orientation

OutputExtension

  • OutputExtension::JPEG - JPEG format
  • OutputExtension::PNG - PNG format
  • OutputExtension::WEBP - WebP format
  • OutputExtension::AVIF - AVIF format
  • OutputExtension::GIF - GIF format
  • OutputExtension::ICO - ICO format
  • OutputExtension::SVG - SVG format
  • OutputExtension::HEIC - HEIC format
  • OutputExtension::BMP - BMP format
  • OutputExtension::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.