PHPackages                             fomvasss/laravel-imagepresets - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. fomvasss/laravel-imagepresets

ActiveLibrary[Image &amp; Media](/categories/media)

fomvasss/laravel-imagepresets
=============================

Image presets (on-the-fly resize/convert) for Laravel via League Glide

1.16.0(1mo ago)10135↓77.8%MITPHPPHP ^8.1

Since May 2Pushed 1mo agoCompare

[ Source](https://github.com/fomvasss/laravel-imagepresets)[ Packagist](https://packagist.org/packages/fomvasss/laravel-imagepresets)[ Fund](https://send.monobank.ua/jar/5xsqtHvVrY)[ Fund](https://ko-fi.com/fomvasss)[ RSS](/packages/fomvasss-laravel-imagepresets/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (7)Versions (21)Used By (0)

fomvasss/laravel-imagepresets
=============================

[](#fomvassslaravel-imagepresets)

> 🇺🇦 [Документація українською](README.uk.md)

On-the-fly image resizing, converting and caching for Laravel, powered by [League/Glide](https://glide.thephpleague.com/).

[![PHP](https://camo.githubusercontent.com/83dd395020c37276225039739320f6c8e7e99963ab21ee3d09282cb48dad2a60/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532422d626c7565)](https://www.php.net/)[![Laravel](https://camo.githubusercontent.com/41aa769949811e4509b48bd6ab3e063ea8e0d618209d6a637059401d629c02d3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31302b2d726564)](https://laravel.com/)[![Latest Stable Version](https://camo.githubusercontent.com/f06105a9e7d9fbb92f85f8572a3200e3b9f9ceba2029b5abf74c818d118bf4df/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f666f6d76617373732f6c61726176656c2d696d616765707265736574732e737667)](https://packagist.org/packages/fomvasss/laravel-imagepresets)[![Build Status](https://camo.githubusercontent.com/30d7022ee749163a95c862dc05e38b65d327a6bcdfac52fab2abfd83bcfd1103/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f666f6d76617373732f6c61726176656c2d696d616765707265736574732e7376673f7374796c653d666f722d746865)](https://github.com/fomvasss/laravel-imagepresets)[![Total Downloads](https://camo.githubusercontent.com/094fdbc0cad2388348a50bf2eebdc2ac01a92c7e041cbe1f3c5cb6071aac4389/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f666f6d76617373732f6c61726176656c2d696d616765707265736574732e737667)](https://packagist.org/packages/fomvasss/laravel-imagepresets)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

---

Support
-------

[](#support)

If this package is useful to you, consider supporting its development:

[![Monobank](https://camo.githubusercontent.com/f4ca1cf4fd0a99ddfed314cb8f65140d8e83e324bd7c1f8476c52fe4ee6b25fd/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f6e6174652d4d6f6e6f62616e6b2d626c61636b)](https://send.monobank.ua/jar/5xsqtHvVrY)[![Ko-Fi](https://camo.githubusercontent.com/10764ec41c79959f08ccaf3e066c7a76c371961b1b1ff5b5f2a33aceef6e857a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f6e6174652d4b6f2d2d66692d4646354535423f6c6f676f3d6b6f2d6669266c6f676f436f6c6f723d7768697465)](https://ko-fi.com/fomvasss)[![USDT TRC20](https://camo.githubusercontent.com/214b4dc05bedda1c5129726b4450ef7f672ce6637865f8a8be83c03582bfdce8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f6e6174652d5553445425323054524332302d3236413137423f6c6f676f3d746574686572266c6f676f436f6c6f723d7768697465)](https://link.trustwallet.com/send?coin=195&address=THLgp6DxiAtbNHvgnKV56vk1L38UuUagKf&token_id=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t)

> USDT TRC20 address: `THLgp6DxiAtbNHvgnKV56vk1L38UuUagKf`

---

Features
--------

[](#features)

- On-the-fly resize, crop and format conversion (WebP, AVIF, JPG, PNG, GIF)
- Automatic caching of processed images to any Laravel filesystem disk (local, S3, GCS, FTP, etc.)
- Remote disk support (S3 / GCS / FTP) — Glide processes locally, result is uploaded automatically
- SVG passthrough with optional XSS sanitization
- Remote image support with SSRF and image-bomb protection
- Race condition protection via Cache lock (Redis/Memcached)
- Auto-registered route — no manual setup required
- Facade + global helper + Blade directive
- Artisan command `imagepresets:clear`
- Fully configurable via `config/imagepresets.php`

---

Requirements
------------

[](#requirements)

DependencyVersionPHP^8.1Laravel10 / 11 / 12 / 13league/glide^2.0 | ^3.0Optional:

- `imagick` PHP extension — required for AVIF output and SVG rasterization
- `enshrined/svg-sanitize` — full SVG sanitization (recommended)

---

Installation
------------

[](#installation)

```
composer require fomvasss/laravel-imagepresets
```

The service provider is auto-discovered via Laravel's package discovery.

### Publish configuration

[](#publish-configuration)

```
php artisan vendor:publish --tag=imagepresets-config
```

This creates `config/imagepresets.php` in your application.

---

Configuration
-------------

[](#configuration)

Key options in `config/imagepresets.php`:

```
// Route
'route' => [
    'prefix'     => env('IMAGEPRESET_ROUTE_PREFIX', 'imagepreset'),
    'name'       => env('IMAGEPRESET_ROUTE_NAME', 'imagepreset'),
    'middleware' => ['throttle:240,1'],
],

// Storage disk and subdirectory for cached presets
'disk' => env('IMAGEPRESET_DISK', 'public'),  // or 's3', 'gcs', etc.
'path' => env('IMAGEPRESET_PATH', ''),

// Processing driver: 'gd' or 'imagick'
'driver' => env('IMAGEPRESET_DRIVER', 'gd'),

// Default output quality and format
'quality' => 80,
'format'  => 'webp',

// HTTP cache lifetime (seconds)
'cache_max_age' => 31536000,

// Allowed dimensions, qualities, fit methods, formats
// Use ['*'] as a wildcard to allow any value (no restriction)
'allowed_widths'    => [100, 200, 300, 400, 600, 800, 1000, 1200, 1600],
'allowed_heights'   => [100, 200, 300, 400, 600, 800],
'allowed_sizes'     => [[300, 200], [600, 400], [1200, 800]],
'allowed_qualities' => [50, 60, 70, 80, 90, 100],
'allowed_fits'      => ['contain', 'crop', 'fill', 'fill-max', 'max', 'stretch'],
'allowed_formats'   => ['webp', 'jpg', 'png', 'gif'],

// SVG optionslaravel-imagepresets
'svg' => [
    'sanitize'                 => true,
    'remove_remote_references' => true,
    'rasterize'                => false, // requires driver=imagick
],

// Remote image protection
'max_download_bytes' => 20 * 1024 * 1024,
'max_image_pixels'   => 150_000_000, // ~150 Mpx image-bomb protection
'allowed_hosts'      => [], // e.g. ['cdn.example.com']

// Local Glide working dir when using a remote disk (S3/GCS/FTP)
'local_cache_dir' => storage_path('app/imagepreset_glide_cache'),
```

> **Cache Lock:** For correct multi-server behaviour set `CACHE_DRIVER=redis` in `.env`. The `file` driver only locks within a single PHP process.

### Remote disk (S3 / GCS / FTP)

[](#remote-disk-s3--gcs--ftp)

Set the disk in `.env` — the package detects it automatically:

```
IMAGEPRESET_DISK=s3
IMAGEPRESET_PATH=imagepresets
```

Processing flow for remote disks:

1. Glide processes the image into `local_cache_dir` (local)
2. The result is uploaded to the remote disk via Flysystem
3. The local file is deleted
4. The response is streamed directly from the remote disk

```
# Clear S3 preset cache
php artisan imagepresets:clear --disk=s3
```

---

Usage
-----

[](#usage)

### Endpoint

[](#endpoint)

```
GET /imagepreset?src=...&w=...&h=...&q=...&fm=...&fit=...

```

### Query parameters

[](#query-parameters)

ParameterTypeDescription`src`string**Required.** Relative path or remote URL of the source image`preset`stringNamed preset defined in `config/imagepresets.php` (presets section)`w`intOutput width in pixels (must be in `allowed_widths`, or any if `['*']`)`h`intOutput height in pixels (must be in `allowed_heights`, or any if `['*']`)`q`intQuality 1–100 (must be in `allowed_qualities`, or any if `['*']`)`fm`stringOutput format: `webp`, `jpg`, `png`, `gif`, `avif``fit`stringFit method: `contain`, `crop`, `fill`, `fill-max`, `max`, `stretch``blur`intBlur radius `0–100``sharp`intSharpen amount `0–100``or`stringOrientation: `auto` (EXIF), `0`, `90`, `180`, `270``crop`stringCoordinate crop: `w,h,x,y` — e.g. `200,200,10,10``bg`stringBackground fill colour (hex without `#`): `fff`, `ff5733`When both `w` and `h` are passed, the pair must be listed in `allowed_sizes` (unless `allowed_sizes = ['*']`).

### Fit methods

[](#fit-methods)

ValueDescription`contain`Scales the image to fit within `w`×`h`, preserving aspect ratio. No cropping. Transparent/empty space is **not** filled.`max`Same as `contain` but never upscales beyond the original dimensions.`fill`Scales the image to fill the entire `w`×`h` canvas. Empty space is filled with the `bg` colour. May upscale small images.`fill-max`Same as `fill` but **never upscales** — if the image is smaller than the canvas it is centred and the remaining space is filled with `bg`. Equivalent to Spatie MediaLibrary's `Fit::FillMax`.`crop`Scales and **crops** the image to exactly `w`×`h`. No empty space, but edges may be trimmed.`stretch`Stretches the image to exactly `w`×`h` ignoring the aspect ratio.> **`fill-max` vs `crop`:** use `fill-max` when the full image must remain visible (e.g. og:image banners, product feeds); use `crop` when exact pixel dimensions are required and trimming is acceptable.

```
// The full image is visible; white padding fills the remaining canvas area
$url = imagepreset_url('photo.jpg', ['w' => 1300, 'h' => 650, 'fit' => 'fill-max', 'bg' => 'ffffff', 'fm' => 'jpg']);
```

### Wildcard mode

[](#wildcard-mode)

Set any of the `allowed_*` config keys to `['*']` to disable the corresponding restriction entirely:

```
'allowed_widths'    => ['*'], // any width is accepted
'allowed_heights'   => ['*'], // any height is accepted
'allowed_sizes'     => ['*'], // any w+h pair is accepted
'allowed_qualities' => ['*'], // any quality value 1–100 is accepted
```

> **Note:** Base HTTP validation limits still apply — `w` and `h` are capped at `20000`, `q` must be an integer.

### Helper function

[](#helper-function)

```
$url = imagepreset_url('storage/images/photo.jpg', ['w' => 800, 'fm' => 'webp']);
// → https://example.com/imagepreset?fm=webp&src=storage%2Fimages%2Fphoto.jpg&w=800
```

```
$url = imagepreset_url('https://example.com/storage/images/photo.jpg', ['w' => 800, 'fm' => 'webp']);
// → https://example.com/imagepreset?fm=webp&src=https://example.com/storage%2Fimages%2Fphoto.jpg&w=800
```

---

Named Presets
-------------

[](#named-presets)

Define reusable named presets in `config/imagepresets.php`:

```
'presets' => [
    'thumb'  => ['w' => 300, 'h' => 200, 'fm' => 'webp', 'q' => 80, 'fit' => 'crop'],
    'hero'   => ['w' => 1200, 'fm' => 'webp', 'q' => 85],
    'avatar' => ['w' => 96, 'h' => 96, 'fm' => 'webp', 'fit' => 'crop'],

    // og:image social banner — fill-max keeps the full image, fills gaps with bg colour
    'og_banner' => ['w' => 1300, 'h' => 650, 'fit' => 'fill-max', 'fm' => 'jpg', 'q' => 85, 'bg' => 'ffffff'],
],
```

Use a preset by name:

```
// Helper — shorthand string
$url = imagepreset_url('photo.jpg', 'thumb');

// Helper — array key
$url = imagepreset_url('photo.jpg', ['preset' => 'hero']);

// Facade
Imagepreset::url('photo.jpg', 'avatar');

// Blade directive

// HTML endpoint

```

Explicit params passed alongside a preset **override** the preset defaults:

```
// Uses thumb preset but overrides format to jpg
$url = imagepreset_url('photo.jpg', ['preset' => 'thumb', 'fm' => 'jpg']);
```

> **Security:** preset params come from trusted config and bypass `allowed_widths` / `allowed_heights` / `allowed_sizes` / `allowed_qualities` checks. Explicit override params are still validated against the allowlists.

### Facade

[](#facade)

```
use Fomvasss\Imagepresets\Facades\Imagepreset;

$url = Imagepreset::url('storage/images/photo.jpg', ['w' => 400, 'h' => 300]);
```

### Blade directive

[](#blade-directive)

```

```

### HTML example

[](#html-example)

```

```

---

SVG Support
-----------

[](#svg-support)

SVG files are passed through without dimension transformations. The cache key is based solely on `src` to avoid duplicates.

```
// SVG is cached and served as-is (sanitized by default)
$url = imagepreset_url('storage/icons/logo.svg');
```

Enable sanitization (recommended) in config:

```
'svg' => [
    'sanitize' => true,
],
```

For full sanitization install the optional dependency:

```
composer require enshrined/svg-sanitize
```

Without it, a basic regex sanitizer removes `` tags, `on*` event attributes, and `javascript:` URIs.

### SVG rasterization

[](#svg-rasterization)

To convert SVG to raster when `w`, `h` or `fm` is passed:

```
'svg' => [
    'rasterize' => true, // requires driver=imagick
],
```

---

Remote Images
-------------

[](#remote-images)

Pass any allowed external URL as `src`:

```
$url = imagepreset_url('https://cdn.example.com/photo.jpg', ['w' => 400]);
```

Allowed hosts must be declared in config:

```
'allowed_hosts' => [
    'cdn.example.com',
],
```

**Security measures:**

- HTTP redirects are blocked (SSRF protection)
- Private and reserved IP ranges are rejected
- `localhost` is rejected
- Maximum download size: `max_download_bytes`
- Maximum pixel area: `max_image_pixels` (image-bomb protection)

---

Artisan Commands
----------------

[](#artisan-commands)

### Clear preset cache

[](#clear-preset-cache)

```
php artisan imagepresets:clear
```

Options:

OptionDescription`--disk=`Override disk (default: `imagepresets.disk` config)`--path=`Override path (default: `imagepresets.path` config)`--temp`Also clear `source_dir` and `temp_dir````
# Clear cache + temp directories
php artisan imagepresets:clear --temp

# Clear a custom disk/path
php artisan imagepresets:clear --disk=s3 --path=presets
```

---

Audit Log (discovering required sizes)
--------------------------------------

[](#audit-log-discovering-required-sizes)

Use audit logging in `local` / `staging` environments to discover which image params the frontend actually requests — then promote them to explicit allowlists for production.

### Workflow

[](#workflow)

1. Enable wildcard mode + audit log in `.env` (non-production only):

```
IMAGEPRESET_AUDIT_LOG=true
# IMAGEPRESET_AUDIT_LOG_ONLY_NEW=true  # log only cache misses (first generation)
```

> Entries are written to the application default log channel (`LOG_CHANNEL` in `.env`).

2. Let the frontend work freely — every new param combination is logged:

```
{"message":"imagepreset_request","context":{"params":{"src":"products/photo.jpg","w":640,"fm":"webp"},"ip":"127.0.0.1","url":"http://app.test/imagepreset?src=..."}}
```

3. Analyse the log to collect unique combinations:

```
# All unique w values requested
grep -oh '"w":[0-9]*' storage/logs/*.log | sort -u

# All unique [w, h] pairs
grep -oh '"w":[0-9]*,"h":[0-9]*' storage/logs/*.log | sort -u

# All unique quality values
grep -oh '"q":[0-9]*' storage/logs/*.log | sort -u
```

4. Promote findings to explicit allowlists in `config/imagepresets.php` and disable wildcard + audit log before deploying to production:

```
'allowed_widths'    => [320, 640, 960, 1280],
'allowed_heights'   => [200, 400],
'allowed_sizes'     => [[640, 400], [1280, 800]],
'allowed_qualities' => [80, 90],
```

```
# .env (production)
IMAGEPRESET_AUDIT_LOG=false
```

### Config reference

[](#config-reference)

KeyDefaultDescription`audit_log.enabled``false`Enable/disable via `IMAGEPRESET_AUDIT_LOG``audit_log.only_new``true`Log only cache misses — skip already-cached combinations---

Excluding Preset Cache from Backups
-----------------------------------

[](#excluding-preset-cache-from-backups)

The `/imagepreset` cache directory contains auto-generated files that can always be recreated on demand. Including it in backups wastes storage and increases backup time.

### Recommended: separate disk outside the backup scope

[](#recommended-separate-disk-outside-the-backup-scope)

Define a dedicated filesystem disk that lives **outside** your regular backup directory:

```
// config/filesystems.php
'imagepresets' => [
    'driver' => 'local',
    'root'   => storage_path('app/imagepresets'), // not inside app/public
    // 'url'    => env('APP_URL').'/imagepresets', // no need for a URL since this is only a temporary working dir for Glide
    'visibility' => 'public',
    'throw'      => false,
],
```

```
# .env
IMAGEPRESET_DISK=imagepresets
```

Now the cache folder is completely outside `storage/app/public` and will never appear in backups that include only `storage_path('app/public')`.

> **Note:** The cache directory is recreated automatically on the first request for each preset — no manual intervention is needed after a restore.

---

HTTP Caching &amp; CDN / Reverse Proxy
--------------------------------------

[](#http-caching--cdn--reverse-proxy)

Every response from the `/imagepreset` endpoint includes headers optimised for aggressive edge caching:

```
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable
ETag: ""
Last-Modified:

```

### Nginx

[](#nginx)

Cache processed presets directly on the server — Laravel is bypassed on subsequent requests:

```
proxy_cache_path /var/cache/nginx/imagepresets
    levels=1:2
    keys_zone=imagepresets:20m
    max_size=2g
    inactive=365d
    use_temp_path=off;

server {
    # ...

    location /imagepreset {
        proxy_cache            imagepresets;
        proxy_cache_valid      200 365d;
        proxy_cache_use_stale  error timeout updating http_500 http_502 http_503;
        proxy_cache_lock       on;                    # prevents cache stampede
        proxy_cache_key        "$scheme$host$request_uri";
        add_header             X-Cache-Status $upstream_cache_status;

        proxy_pass http://127.0.0.1:9000;             # your PHP-FPM / app
    }
}
```

#### Verifying the cache

[](#verifying-the-cache)

Make the same request twice using GET — the first returns `MISS`, the second must return `HIT`. This command works for both **Nginx** (`X-Cache-Status`) and **Cloudflare** (`cf-cache-status`):

```
# Sends GET, discards body, prints response headers
curl -s -o /dev/null -D - "https://example.com/imagepreset?src=photo.jpg&w=800&fm=webp"
```

Expected response headers:

```
X-Cache-Status: HIT        # Nginx proxy cache
cf-cache-status: HIT       # Cloudflare edge cache

```

> **Note:** `curl -I` sends a **HEAD** request — Cloudflare does not cache HEAD and always returns `cf-cache-status: DYNAMIC`. Always use a GET request to verify caching.

`X-Cache-Status`Meaning`MISS`No cache entry — request went to PHP`HIT`Served from Nginx cache, PHP was not invoked`EXPIRED`Cache entry exists but expired — being refreshed`BYPASS`Caching was skipped`cf-cache-status`Meaning`MISS`Cloudflare has no cache — request forwarded to origin`HIT`Served from Cloudflare edge cache`DYNAMIC`Not cached — HEAD request or no Cache Rule configured`EXPIRED`Cache expired — being refreshed from originCompare response times — a `HIT` is typically 10–100× faster:

```
time curl -s "https://example.com/imagepreset?src=photo.jpg&w=800" -o /dev/null
```

### Cloudflare

[](#cloudflare)

Add a Cache Rule in the Cloudflare dashboard:

- **If** → URI Path starts with `/imagepresets`
- **Then** → Cache Level: Cache Everything, Edge Cache TTL: 1 year

Or via Terraform / API:

```
{
  "description": "Cache imagepresets",
  "expression": "(http.request.uri.path starts_with \"/imagepreset\")",
  "action": "set_cache_settings",
  "action_parameters": {
    "cache": true,
    "edge_ttl": { "mode": "override_origin", "default": 31536000 },
    "browser_ttl": { "mode": "override_origin", "default": 31536000 }
  }
}
```

### Cache invalidation

[](#cache-invalidation)

When you change a preset definition or replace a source image, the cached files must be purged:

```
# Clear the Laravel-level disk cache (always required)
php artisan imagepresets:clear

# Nginx — reload or flush proxy cache directory
find /var/cache/nginx/imagepresets -type f -delete

# Cloudflare — purge by prefix via API
curl -X POST "https://api.cloudflare.com/client/v4/zones/{ZONE_ID}/purge_cache" \
     -H "Authorization: Bearer {TOKEN}" \
     -H "Content-Type: application/json" \
     --data '{"prefixes":["https://example.com/imagepreset"]}'
```

> **Tip:** Use versioned `src` paths (e.g. `photo_v2.jpg`) or append a query param (`?v=2`) to bust the cache without a full purge.

---

Response Headers
----------------

[](#response-headers)

Every response includes:

HeaderValue`Content-Type`Correct MIME type`Cache-Control`See [HTTP Caching](#http-caching) section below`ETag`Based on file mtime + size`Last-Modified`File modification time`Content-Disposition``inline``X-Content-Type-Options``nosniff``Content-Security-Policy`SVG only: `default-src 'none'; style-src 'unsafe-inline'; sandbox`### HTTP Caching

[](#http-caching)

The package implements **intelligent cache headers** based on file generation state:

**Newly generated files (first request):**

```
Cache-Control: no-store

```

- File was just created — may have issues
- Prevents aggressive browser/CDN caching
- Next request will re-validate

**Cached files (subsequent requests):**

```
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable

```

- File exists and is stable
- URL contains MD5 hash of all parameters — content never changes for same URL
- Safe to cache for 1 year in browser and CDN (Cloudflare, Fastly, Akamai, etc.)
- `immutable` signals the URL is content-addressed — file will never change

**Configuration:**

```
// config/imagepresets.php
'cache_max_age' => env('IMAGEPRESET_CACHE_MAX_AGE', 31536000),  // 1 year in seconds
```

This approach minimizes bandwidth and server load while maintaining reliability during the generation phase.

---

Security
--------

[](#security)

ThreatProtectionPath traversal`..` and null bytes rejected in `src`SSRF via remote URLPrivate/reserved IPs + localhost blocked; redirects disabledImage bombPixel area check (`max_image_pixels`) for both local and remote filesSVG XSSSanitization via `enshrined/svg-sanitize` or regex fallback; CSP headerCache stampede`Cache::lock()` with double-check after acquiringContent sniffing`X-Content-Type-Options: nosniff`Arbitrary URL generationOptional Signed URL (see below)---

Signed URL (optional)
---------------------

[](#signed-url-optional)

By default the endpoint is open — any request that passes whitelist validation is processed. Enable Signed URL to restrict access **only** to URLs generated by the server.

### Enable

[](#enable)

```
# .env
IMAGEPRESET_SIGNED_URL=true
```

Or in `config/imagepresets.php`:

```
'route' => [
    // ...
    'signed' => true,
],
```

### How it works

[](#how-it-works)

- `imagepreset_url()`, `Imagepreset::url()` and `@imagepreset()` automatically generate a permanent signed URL via `URL::signedRoute()`.
- The `signed` middleware validates the HMAC signature on every request.
- Requests without a valid signature return **403 Forbidden**.
- Tampering with any parameter (e.g. changing `w=300` to `w=9999`) invalidates the signature.

### Example

[](#example)

```
// Generates: https://example.com/imagepreset?fm=webp&signature=...&src=photo.jpg&w=800
$url = imagepreset_url('photo.jpg', ['w' => 800, 'fm' => 'webp']);
```

```

```

### Notes

[](#notes)

- Signed URLs are **permanent** (no expiry). This makes them fully compatible with CDN / reverse-proxy caching — no extra CDN configuration required.
- When `signed = false` (default) behaviour is identical to previous versions — **fully backwards-compatible**.
- The signature is based on `APP_KEY`. Rotating the key invalidates all previously generated URLs.

---

Testing
-------

[](#testing)

```
composer test
# or
vendor/bin/phpunit
```

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance91

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

20

Last Release

34d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4f99b460639e7d6871597882226b1e0d9e3055992eac8b22bfcd4f90fbdac95c?d=identicon)[fomvasss](/maintainers/fomvasss)

---

Top Contributors

[![fomvasss](https://avatars.githubusercontent.com/u/19834478?v=4)](https://github.com/fomvasss "fomvasss (27 commits)")

---

Tags

laravelimageresizeglideWebppresetfomvasss

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/fomvasss-laravel-imagepresets/health.svg)

```
[![Health](https://phpackages.com/badges/fomvasss-laravel-imagepresets/health.svg)](https://phpackages.com/packages/fomvasss-laravel-imagepresets)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[intervention/image-laravel

Laravel Integration of Intervention Image

1588.9M201](/packages/intervention-image-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M133](/packages/roots-acorn)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77922.3M186](/packages/laravel-mcp)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
