How to Optimize OpenCart Images from the Command Line

php dev.to

OpenCart stores have a sneaky disk problem. You add products one by one through the admin, each with a couple of photos, and it feels small. But after two years of daily catalog updates, image/catalog/ has ballooned to several gigabytes and nobody noticed because nobody checks. Then a PageSpeed audit lands on your desk and suddenly those uncompressed JPEGs are everyone's priority.

You could install an OpenCart image extension, but extensions handle future uploads. They don't retroactively shrink the thousands of oversized files already sitting on your server. For that, you need something that can chew through an entire directory tree in one shot.

ShortPixel Optimizer CLI does exactly that. It's a bash script, one file, no PHP dependencies, no modules to hook into OpenCart, that sends images to the ShortPixel compression API, downloads the optimized versions, and tracks everything it's done so it never double-processes a file. All it requires is bash and curl, which every Linux host already has.

Here's how I set it up for OpenCart, what folders to hit, what to skip, and how to keep it running automatically.

I've been using this approach on a few OpenCart cleanups lately, especially for stores with large image libraries. If you're working with other platforms, I've covered similar setups for PrestaShop and Magento as well.

OpenCart's image folder layout

Understanding the filesystem helps you avoid wasting image optimization credits on throwaway files. An OpenCart installation splits its images into two areas:

image/
├── catalog/        ← your actual uploads (products, categories, banners, blog posts)
│   ├── products/
│   ├── demo/
│   └── ...
├── cache/          ← throwaway thumbnails OpenCart generates on the fly
│   └── catalog/
│       └── products/
│           ├── red-sneakers-80x80.jpg
│           ├── red-sneakers-228x228.jpg
│           └── red-sneakers-500x500.jpg
└── no_image.png
Enter fullscreen mode Exit fullscreen mode

image/catalog/ is the source of truth. Every photo you or your team uploaded through the admin's Image Manager ends up here. Subfolders vary, some stores use products/, others have custom directories, some toss everything flat into catalog/.

image/cache/ is generated automatically. OpenCart's resize logic (inside model/tool/image.php) checks whether a cached thumbnail exists at the requested dimensions. If it doesn't, it creates one from the original and saves it with the size appended to the filename. A single product photo can spawn six or seven cached variants depending on how many thumbnail sizes your theme defines. It's not unusual for image/cache/ to be 3–5× larger than image/catalog/.

The rule: optimize image/catalog/. Leave image/cache/ alone. Once your originals are smaller, you clear the cache, and OpenCart rebuilds every thumbnail from the leaner sources. Compressing cache files directly is pointless because any admin who clicks "Refresh Image Cache" (or any theme update) wipes them out.

Getting the script onto your server

Connect via SSH and grab the repository anywhere outside the public webroot:

cd ~
git clone https://github.com/short-pixel-optimizer/shortpixel-sh.git
cd shortpixel-sh
chmod +x shortpixel-optimize.sh
Enter fullscreen mode Exit fullscreen mode

If this is your first time running the script and there's no .env file yet, a setup wizard kicks in. It asks for your ShortPixel API Key, preferred compression level (lossy/lossless/glossy), how many parallel workers to run, where to store backups, and whether you want email reports after each execution. Every question has a default, press Enter to accept it.

Alternatively, create the config file yourself and skip the wizard entirely:

API_KEY=XXXXXXXXXXXXXXXXXXXXXXXX
LOSSY=1
CONCURRENCY=4
BACKUP_DIR=/home/deploy/image-backups
EMAIL=admin@myshop.com
Enter fullscreen mode Exit fullscreen mode

Testing on a small folder first

Don't jump straight to the full catalog. Pick a subfolder with a handful of images, category banners or a small product group, and do a trial run:

./shortpixel-optimize.sh -j 4 /var/www/myshop/image/catalog/banners
Enter fullscreen mode Exit fullscreen mode

When it finishes, you'll see a summary showing how many files were processed, how many bytes were saved, and whether anything failed. This confirms your API key works, the server can reach ShortPixel's endpoint, and the permissions are correct.

By default (without --overwrite), compressed files go into an optimized/ subfolder next to the originals. Open a few and compare quality before going further.

Running the full catalog sweep

Once the test looks good, go wide:

./shortpixel-optimize.sh --overwrite -j 4 /var/www/myshop/image/catalog
Enter fullscreen mode Exit fullscreen mode

With --overwrite, the script replaces each original in place, but only after copying it to the backup directory first. If the backup copy can't be verified (missing or zero bytes), that file gets skipped entirely. Your source is never modified without a safety net.

The script drops a .splog file inside every folder it touches. This is a flat text log (pipe-delimited) recording each file's checksum, original size, compressed size, and timestamp. On subsequent runs, any file that already has a .splog entry gets skipped automatically. This means you can stop and restart whenever you like, progress is never lost, and you never pay twice for the same image.

After the sweep completes, flush OpenCart's image cache so thumbnails get rebuilt from the now-smaller originals:

rm -rf /var/www/myshop/image/cache/*
Enter fullscreen mode Exit fullscreen mode

Or do it through the admin panel: Dashboard → gear icon → Image Cache, or System → Maintenance depending on your OpenCart version. The next page load triggers fresh thumbnail generation.

Adding WebP to the mix

Compression alone is a solid win, but serving WebP to browsers that support it is where you'll see the biggest jump in Core Web Vitals. The script can produce WebP copies alongside every processed file:

./shortpixel-optimize.sh \
  --overwrite \
  --convertto +webp \
  -j 4 \
  /var/www/myshop/image/catalog
Enter fullscreen mode Exit fullscreen mode

The +webp flag creates a .webp sibling for each image, so sneakers.jpg gets a sneakers.webp next to it. The original stays untouched (aside from lossy compression).

Getting OpenCart to actually serve those WebP files requires a small server-side rule. On nginx:

map $http_accept $webp_ext {
    default        "";
    "~*webp"       ".webp";
}

location ~* ^/image/.+\.(jpe?g|png)$ {
    add_header Vary Accept;
    try_files $uri$webp_ext $uri =404;
}
Enter fullscreen mode Exit fullscreen mode

On Apache, an equivalent mod_rewrite rule in .htaccess that checks the Accept header achieves the same result. The generation side (ShortPixel) and the serving side (your web server) are completely independent, once .webp files exist on disk, it's purely a routing question.

Making it run every night

Manual sweeps are fine for the initial cleanup, but you want new uploads handled automatically. Since the .splog mechanism makes every run idempotent, scheduling a nightly job means only freshly added images consume image optimization credits:

30 2 * * * cd /home/deploy/shortpixel-sh && ./shortpixel-optimize.sh --overwrite --convertto +webp -j 4 /var/www/myshop/image/catalog >> /var/log/sp-optimize.log 2>&1 && rm -rf /var/www/myshop/image/cache/*
Enter fullscreen mode Exit fullscreen mode

The cache wipe is chained at the end so that any newly optimized originals produce fresh thumbnails on the next visitor request.

A couple of practical details for unattended execution:

Without a terminal attached, the setup wizard does nothing, it detects the missing TTY and exits cleanly. Your cron will never stall on an interactive prompt.

When EMAIL is configured in .env, a summary report gets sent after every run. The script checks for mail first, then sendmail. You can override this with MAIL_CMD if your host uses something else.

That nightly email is a surprisingly useful early warning system. A normal report shows a few new files processed each day (whatever the merchandising team added). The morning you see zero files or a wall of errors, you know something changed, permissions, disk space, a hosting migration that broke outbound API calls, before any customer notices slower load times.

Rolling back if something looks wrong

Maybe you ran glossy compression and some product shots on white backgrounds show banding. Maybe the client prefers the originals for a specific category. Either way, reverting is a single command:

./shortpixel-optimize.sh --restore /var/www/myshop/image/catalog
Enter fullscreen mode Exit fullscreen mode

Every backed-up original gets copied back to its source location, a restore_audit.log records what was restored, and all .splog files are removed so the next run starts fresh. Clear the image cache afterward and you're back to square one.

When you're satisfied that the optimized images are keepers and you want the backup disk space back:

./shortpixel-optimize.sh --purge-backups 21 /var/www/myshop/image/catalog
Enter fullscreen mode Exit fullscreen mode

Only files that pass two checks get deleted: they must be older than 21 days, and they must have a corresponding entry in .splog. Anything that wasn't successfully processed — skipped files, errors, interrupted runs, stays in the backup tree no matter how old it is.

A quick checklist for new OpenCart projects

Here's the sequence I follow when picking up a store with an unoptimized image library:

  1. Grab the script — clone, chmod, run the wizard or drop a .env.
  2. Snapshot firstrsync image/ to a safe location, separate from the script's own backups.
  3. Trial run — pick a small subfolder, run without --overwrite, compare output quality.
  4. Full pass — sweep image/catalog/ with --overwrite --convertto +webp.
  5. Flush the cache — delete image/cache/* so OpenCart regenerates smaller thumbnails.
  6. Configure WebP serving — add the nginx or Apache content-negotiation rule.
  7. Schedule it — nightly cron with cache wipe chained at the end.
  8. Prune backups — after a couple of weeks of clean reports, reclaim the space.

The active work takes maybe 10 minutes. The initial sweep runs in the background. After that, everything is hands-off, new product photos get compressed overnight, WebP copies appear automatically, and the nightly email tells you if anything needs attention.

The full tool with documentation is available here.

If you're dealing with a bloated OpenCart image folder, this is one of the fastest wins you can get without touching your frontend.

If you're optimizing images across different platforms, check out my other guides on PrestaShop and Magento as well.

Source: dev.to

arrow_back Back to Tutorials