Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Image Optimizer Laravel Package

approached/laravel-image-optimizer

Laravel Image Optimizer automatically compresses uploaded images using configurable optimizers like JPEGoptim, OptiPNG, and SVGO. It integrates with Laravel storage and lets you define profiles for smaller, faster-loading images.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the deprecation. This package is archived/unmaintained. Before you invest in it, confirm it installs against your Laravel/PHP version — the composer.json constraints are old and may not resolve on Laravel 10+. If it does install, treat it as a thin, stable wrapper: it shells out to native binaries and does one thing (compress files in place). If it doesn't install, the maintained path is spatie/image-optimizer directly, which this package wraps anyway.

Install and publish config

composer require approached/laravel-image-optimizer
php artisan vendor:publish --tag=image-optimizer   # check README for exact tag

The service provider is auto-discovered. The published config (config/image-optimizer.php) is the whole package in one array: a map of file extension → list of optimizers to chain.

Install the binaries — this is the real setup step. The PHP side is trivial; the package is useless without the CLI tools it drives:

# Debian/Ubuntu
apt-get install jpegoptim optipng pngquant gifsicle
npm install -g svgo

Verify with which jpegoptim optipng pngquant gifsicle svgo as the same user that runs PHP-FPM/queue workers, not as root in your shell.

First use case: compress an uploaded image before it lands on disk.

use Approached\LaravelImageOptimizer\ImageOptimizer;

public function store(Request $request)
{
    $path = $request->file('photo')->store('uploads', 'public');

    app(ImageOptimizer::class)->optimize(
        Storage::disk('public')->path($path)
    );

    return $path;
}

Note the pattern: the package wants a local filesystem path, so you resolve it via Storage::disk(...)->path() rather than passing a disk-relative key.

Implementation Patterns

Wrap it in a service, don't scatter calls. The API is small and the binary invocation is slow — centralize it so you can later swap in spatie/image-optimizer or intervention/image without touching controllers.

final class ImageCompressor
{
    public function __construct(private ImageOptimizer $optimizer) {}

    public function compress(string $absolutePath): void
    {
        if (! is_file($absolutePath)) {
            return;
        }

        $this->optimizer->optimize($absolutePath);
    }
}

Push it to a queue. Binary execution is 100ms–2s per file depending on size and tool. Doing it synchronously in an upload request is the most common performance mistake with this package.

ProcessImage::dispatch($path)->onQueue('images');

Batch-backfill with an Artisan command. The day-two task after adoption is always "optimize the 40k images already on disk." A chunked command over your media table (or a File::allFiles() walk) is the standard shape:

foreach (File::allFiles(storage_path('app/public/uploads')) as $file) {
    ProcessImage::dispatch($file->getRealPath());
}

Because the tools detect already-optimized files, re-running the backfill is safe and effectively a no-op.

Chain optimizers deliberately. Lossless first, lossy second, is the sane default per format:

  • .jpg → jpegoptim --strip-all (lossless-ish, strips metadata), optionally jpeg-recompress for lossy
  • .png → optipng (lossless) then pngquant (lossy, big wins)
  • .svg → svgo
  • .gif → gifsicle -O3
  • .webp → cwebp

Tune quality knobs in the config, not in call sites — call sites should stay optimize($path).

Pair with a resizer. This package only compresses; it never resizes or converts formats. The idiomatic stack is Intervention Image (or spatie/image) for resize/crop → this package (or spatie's) for compression. Don't expect it to shrink a 6000px photo meaningfully — the bytes saved are from encoder settings, not dimensions.

Local-disk only. Because it needs a real path, S3/R2 flows must download to a temp file, optimize, and upload back. Build that into your service:

$tmp = tempnam(sys_get_temp_dir(), 'img');
file_put_contents($tmp, Storage::disk('s3')->get($key));
$this->compressor->compress($tmp);
Storage::disk('s3')->put($key, fopen($tmp, 'r'));
unlink($tmp);

Custom optimizers are the extension point. If the config format maps to a chain of optimizer classes (as in the underlying spatie package), you can register your own class implementing the optimizer interface for a tool the package doesn't ship (e.g. mozjpeg, avifenc). That's the escape hatch when you need a format or flag the config doesn't expose.

Gotchas and Tips

Silent no-ops when a binary is missing. If pngquant isn't on PATH for the web user, you typically get a skip or a swallowed failure rather than a loud error — images "optimize" successfully but never get smaller. Add a boot-time health check or an assertion in your test suite that each configured binary resolves.

Extension drives optimizer selection. A .jpeg upload with no .jpg entry in the config, or an uppercase .JPG, will silently fall through to "no optimizers." Normalize extensions before handing off, and make sure both jpg and jpeg are configured.

In-place overwrite, no backup. The file is modified where it sits. If you need the original (you usually don't, but for reprocessing pipelines you might), copy it first — there's no --keep-original equivalent here.

Permissions, not code, cause most "it does nothing" reports. PHP-FPM and queue workers run as different users. The binary must be on that user's PATH, and that user needs write access to the file and its directory (the tools often write a temp file alongside the target).

Docker images: bake the binaries in. apt-get install -y jpegoptim optipng pngquant gifsicle plus a Node stage or npm i -g svgo — otherwise production behaves differently from your laptop, which is exactly where the missing-binary problem hides.

Config caching. After editing optimizer lists, run php artisan config:cache (or config:clear locally). A stale cached config pointing at tools you removed is a confusing 20-minute debug.

Non-zero exit codes are normal. Several tools (notably jpegoptim, pngquant) return non-zero when the file is already optimal or when they skip. If you're wrapping the call in your own try/catch, don't treat every non-zero as fatal.

No EXIF orientation handling. Compression won't rotate your images, and --strip-all on jpegoptim will remove the EXIF orientation tag. If you strip metadata on phone photos, apply orientation via your resizer before optimizing, or you'll get sideways images.

Testing and CI. Binaries usually aren't installed on CI runners. Assert on "file still exists and is readable" rather than on exact byte counts, and either install the tools in the CI image or guard the optimization behind an environment flag so the suite stays green.

Concurrency and disk pressure. Firing hundreds of these at once via a queue will saturate CPU and hammer the disk with temp files. Cap the worker count on the images queue.

Plan the exit. Since the package is deprecated, keep the call surface behind your own ImageCompressor interface so migrating to spatie/image-optimizer (same underlying tools, actively maintained) is a one-file change rather than a codebase-wide search-and-replace.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource