Easily Optimize WordPress Images Using WP CLI and Some Binaries

Images make a WordPress site looks good. However, images also make up the bulk of the size of the entire page. Nobody wants to wait for a slow site. There are some tools available such as JpegOptim, Optipng, Pngguant 2, SVGO and Gifsicle. Manually running these tools after WordPress resizing attachment images is tedious job. Luckily for you, with my newly published WP CLI package, optimizing WordPress media library’s PNGs, JPGs, SVGs and GIFs is just one command away.

The Story

One of my client has a very old news site, everything went well until we decided to use a new theme. The new theme uses different thumbnail sizes. Regenerating new thumbnails was easy. However, we found the new thumbnail’s file size is large than before even the actual dimension is smaller. Thus, the site’s page load time ended up much slower than it should be.

Standing on the Shoulder of Giants

Spatie published the image-optimizer composer package helping us passing PNGs, JPGs, SVGs and GIFs to JpegOptim, Optipng, Pngguant 2, SVGO and Gifsicle via PHP. The results are stunning. On average, we see 30% smaller image size, even better than manually throwing the images to ImageOptim. Unfortunately, I can’t disclose my client’s data. Check the conversions example on spatie/image-optimizer‘s readme.

What Optimization does it Apply to the Images?

Update: It is lossy compression! However, the differences are so small that I didn’t notice until checking the documents.

This is the beauty part spatie/image-optimizer. It comes with a sane default configuration which smart enough to decide which tools to use on a particular image. In short, it make the images smallest possible without human eye noticeable (i.e.: lossless). The project readme gives more explanation about its configuration, basically small enough to make Google’s Pagespeed happy.

The Challenge

Turn out finding the full paths on disk of attachment images is surprisingly hard. WordPress only gives the full path of the original file via get_attached_file. With some modification on this StackExchange answer, this is what I come up with:

     * Get all(original and chopped) paths for a single attachment.
     * @see https://wordpress.stackexchange.com/questions/182477/wp-get-attachment-image-src-and-server-path
     * @param int $id Attachment id.
     * @return string[]
    private static function pathsForSingle(int $id): array
        $metadata = wp_get_attachment_metadata($id);
        $originalFilePath = get_attached_file($id, true);
        $sizes = array_keys(
            wp_list_pluck($metadata['sizes'] ?? [], 'file')
        $choppedFilePaths = array_map(function (string $size) use ($id, $originalFilePath): string {
            $info = image_get_intermediate_size($id, $size);
            return str_replace(wp_basename($originalFilePath), $info['file'], $originalFilePath);
        }, $sizes);
        return array_merge($choppedFilePaths, [

If you know a better way do to it, please leave a comment below. Or, submit a pull request via GitHub.

Installing typisttech/image-optimize-command

Time to do some actual work:

  1. First we need to update WP CLI to v1.4.1 or later
  2. Install binaries that spatie/image-optimizer depends on
  3. Install typisttech/image-optimize-command

$ wp cli update

$ sudo apt-get update
$ sudo apt-get install jpegoptim
$ sudo apt-get install optipng
$ sudo apt-get install pngquant
$ sudo apt-get install gifsicle

$ wp package install typisttech/image-optimize-command:@stable

Note that WordPress doesn’t support SVG out of the box. SVGO is omitted here.

Optimize WordPress Attachment Images

Optimizing #WordPress PNGs, JPGs, SVGs and GIFs just one command away Click To Tweet

Well… That’s the easy part:

# Optimize 10 attachments
$ wp image-optimize run --limit=10

Boom! You just reduced 10 attachments’ file sizes including all their smaller thumbnails.

By default, optimized flags (post meta) are given to attachments after optimization. This is to prevent wasting time on re-optimizing an already optimized attachment. If you changed the image files (e.g.: resize / regenerate thumbnail), you must first reset their meta flags by running $ wp image-optimize reset. Example:

$ wp media regenerate --yes
$ wp image-optimize reset --yes
$ wp image-optimize run --limit=9999999

The Pitfalls

WordPress Database is the Single Source of Truth

The command queries WordPress database for attachment information. If the command stopped halfway, most likely you deleted the images from disk but not updated WordPress’ database. Simplest solution is to regenerate thumbnails then optimize again like the above example.

100% CPU Usage

The command comes at a cost. Optimization is CPU intensive. Expect CPU usage rockets up to 100% during optimization. Schedule it to run at late night in small batches. Because we don’t want the server freezes every time we upload a image. On my client site, we created a cron job to run the optimize command for 3 attachments every 5 minutes between 2:00 am to 3:30 am. This is customized for client’s usage. They usually upload ~40 images every day and daily backup take place at 1:00 am. Adjust with your situation. Also checkout how to ensure WP Cron runs on time.


typisttech/image-optimize-command requires WP CLI and the binaries to be installed and it’s a burden for server resources. There are lots SaaS solutions available as WordPress plugins such as WP Smush Pro, EWWW and Imagify. Most of them provide free trial with some limitation such as maximum file size, monthly quota, etc. Do your research to find the best tools.

Help Wanted

The whole project was done in a Friday afternoon. Quite sure there are bugs hiding from my eyesight. Filing bug reports on GitHub would definitely helps.

Besides, we need more test coverage. Pull requests are accepted!