What it does
Image optimization makes a site’s images lighter — on the client’s own server, with the PHP image library that server already has (Imagick, or GD when Imagick is missing). Nothing is uploaded to a third-party service, there is no CDN, and no image leaves the site. It is available on Agency and Scale (the Agency trial includes it) and needs WPClientHub One 2.0 or newer on the site.
For every JPEG and PNG in the media library — the full-size file WordPress serves and every registered size (thumbnail, medium, large, a theme’s own sizes) — it:
- Re-encodes the file, lossy (JPEG at the quality you choose, default 82) or lossless (quality 100 — in practice “metadata stripped, nothing else lost”). A file is only replaced when the result is smaller.
- Strips EXIF and other metadata. The EXIF orientation is applied to the pixels first, so a phone photo stays the right way up once the tag is gone. With Imagick the embedded colour profile (ICC) can be kept; GD cannot keep one.
- Resizes oversized uploads the way WordPress does: an upload whose longer side is above the limit (default 2560 px, WordPress’s own big-image threshold) gets a
-scaledcopy that becomes the served file, and the unscaled original stays on disk untouched. A-scaledfile WordPress already made is compressed but never resized again. - Writes a WebP copy and, where the server’s PHP can encode it, an AVIF copy next to each file (
photo.jpg→photo.jpg.webp,photo.jpg.avif), only when the copy is smaller than the file it stands in for. Images over 16 megapixels get WebP only, because AVIF encoding of very large images is slow. - Keeps the original of every file it changes in a protected folder on the site, so an image — or all of them — can be put back exactly as it was. See Restore.
Savings are measured on the files the site serves. When an upload gets a -scaled copy, the unscaled original WordPress keeps stays on disk, so the disk does not shrink by as much as the “lighter by” number.
Lazy loading is not part of this, on purpose: WordPress has lazy-loaded images on its own since version 5.5 (loading="lazy" on every image below the fold), so there is nothing to add.
Where to find it
Open a site and pick the Images tab (after Performance). The Overview tab’s Images card shows what the images are lighter by and links there. The tab shows:
- The status — On, Handled by plugin, Off, Plugin update needed, Not connected, or the Agency badge on plans below it. A site nobody has switched on yet shows Off — turn on to start: optimization is opt-in, so nothing happens to a site’s images until you choose the settings and switch it on (Choose settings and turn on).
- What the host can do — Imagick or GD, whether WebP and AVIF can be encoded, the PHP memory limit. “AVIF isn’t available on this host” means the server’s PHP image library cannot write AVIF — built without it, or (on some PHP builds) claiming support but writing empty files; the plugin tests a real encode. Ask the host, or accept WebP only.
- Totals — images optimized out of the images in the library, bytes before and after (and the percentage), and how many files have a WebP and an AVIF copy.
- Optimize all images with live progress and a Stop button, the settings, restore, and a history of runs.
When it runs
- New uploads are optimized shortly after WordPress has made their sizes — queued, never inside the upload request, so uploading stays as fast as before.
- Optimize all images goes through the whole media library in small batches of about 20 seconds each, driven from WPClientHub, with the progress on the tab. Images already done with the current settings are skipped; images done with older settings (a different quality, size limit or set of formats) are redone. You can leave the page, and you can stop it — what was done stays done.
- Nightly run (off by default): every night at the hour you choose, in the site’s own timezone, on the site’s own WP-Cron. It does the same as Optimize all. The result shows up on the tab and in the Activity Log after the site’s next sync.
It never runs while a backup or a restore is going on the site, and only one run goes at a time.
Settings
Per site, from the tab’s Settings button. Saving sends the settings to the site as one signed instruction (the same way the white-label settings travel); the tab says Applied once the site has confirmed them, or Pending until its next sync.
| Setting | Default | What it does |
|---|---|---|
| Optimize images on this site | Off | Nothing on a site is compressed, resized or converted until you switch this on (since WPClientHub One 2.0.1; 2.0.0 switched it on at pairing). Off pauses everything — new uploads, the nightly run, and serving the WebP/AVIF copies. Nothing already optimized is changed. |
| Compression | Lossy, quality 82 | Lossy for photos (82 is hard to tell from the original; 40–100). Lossless keeps every pixel and only strips metadata — modest savings. |
| Resize above | 2560 px | The longest side above which an upload gets a -scaled copy (1024–8192, or off to never resize). |
| WebP copies / AVIF copies | Both on | Written next to every file where the host can encode them. Keeping AVIF on where the host cannot make it costs nothing. |
| Keep the originals | On | Every file that is changed is copied first into wp-content/uploads/wpclienthub-originals/. Off saves disk space and makes restores impossible for images done from then on. |
| Keep colour profiles | On | With Imagick the ICC profile survives the metadata strip. GD cannot keep one, so colours of wide-gamut images may shift slightly on GD hosts. |
| Optimize new uploads | On | Queue each new upload after WordPress has made its sizes. |
| Nightly run | Off | A nightly pass over the whole library at the chosen hour, site time. |
| Run anyway | Off | Only shown when another optimizer plugin is active — see below. |
| Serving the copies | Automatic | How browsers get the WebP/AVIF copies — see the next section. |
Roles: owners, admins and techs change the settings and run things; viewers read. Every settings change is in the Activity Log with what changed.
Serving WebP and AVIF
A copy is only useful if browsers get it. In Automatic mode the plugin picks the method for the host:
Apache and LiteSpeed
A marked block in wp-content/uploads/.htaccess rewrites a request for photo.jpg to photo.jpg.avif or photo.jpg.webp when the browser’s Accept header allows that format and the copy exists — with Vary: Accept so caches keep the variants apart. The URLs in the page never change, and a browser that accepts neither gets the JPEG. The block sits next to the uploads-protection block the plugin already maintains and leaves everything else in the file alone.
The plugin then checks that the rules work: it writes a tiny probe image with a .webp sibling, requests it from the site with Accept: image/webp, and looks at the content type that comes back. If the answer is not image/webp (mod_rewrite is off, AllowOverride forbids it, a CDN in front ignores the header), the tab says so under Serving WebP / AVIF and the site falls back to the page rewrite below on its own. Check now on the tab repeats the check.
nginx, and hosts where the rules are not in effect
nginx does not read .htaccess. The plugin instead rewrites <img> tags on the front end into <picture> elements — one <source> per format that exists for every candidate in the tag’s srcset, the original <img> kept as the fallback. Tags inside an existing <picture>, <noscript>, <script> or <template> are left alone, as are images on other hosts and SVGs. This costs a small amount of work on every uncached page view, which is why the header-based method is preferred where it works.
For header-based serving on nginx, the tab offers a snippet to paste into the server configuration (two map blocks in the http context and a location in the site’s server block, above any generic static-files location):
map $http_accept $wpch_avif {
default ".none";
"~*image/avif" ".avif";
}
map $http_accept $wpch_webp {
default ".none";
"~*image/webp" ".webp";
}
location ~* ^\/wp-content\/uploads\/.+\.(jpe?g|png)$ {
add_header Vary Accept;
types { image/avif avif; image/webp webp; image/jpeg jpg jpeg; image/png png; }
try_files $uri$wpch_avif $uri$wpch_webp $uri =404;
}
The .none defaults make try_files skip a format the browser did not ask for instead of serving the original before it has tried the next one. Once the snippet is in place, set Serving the copies to Don’t serve them so the page rewrite stops. The other modes force one method: Rewrite rules in uploads/.htaccess or Rewrite the page.
With Cloudflare (the Scale integration): after a bulk run or a restore on a linked site with auto-purge on, WPClientHub purges the site’s Cloudflare cache the same way it does after updates, so visitors get the new files rather than cached copies. Cloudflare honours Vary: Accept on images only on some plans; the page rewrite does not depend on it.
If another optimizer is already on the site
The plugin recognises Smush (free and Pro), ShortPixel Image Optimizer, Imagify, EWWW Image Optimizer, Optimole and TinyPNG’s “Compress JPEG & PNG images” by their plugin files. When one of them is active, the tab shows the site as Handled by that plugin and WPClientHub’s optimizer stays off by default: two optimizers re-compressing each other’s output only lose quality. Where the other plugin keeps its totals in one place that is cheap to read — Smush’s global savings option and ShortPixel’s per-file table — those totals are shown; otherwise it says “installed, stats unavailable” rather than guessing.
Run anyway in the settings overrides that for one site. Even then, images the other plugin already handled are skipped (it marks them on each attachment), so nothing is compressed twice.
Restore
Every file the optimizer changes is copied first — before the first change, once — into wp-content/uploads/wpclienthub-originals/, which is denied to the web (an .htaccess and a web.config like the backup vault, plus an index file). Deleting an attachment in WordPress deletes its kept original too.
- Restore one image takes the media library ID (it is in the URL of the attachment page). The original file and every size go back exactly as they were, the WebP/AVIF copies are removed, and a
-scaledcopy the optimizer made is removed with the metadata put back. - Restore all does that for every optimized image as a run, with progress and Stop. Optimization stays switched on afterwards unless you turn it off, so new uploads are still optimized — turn it off first if you want the site left alone.
Both are in the Activity Log (with totals, never file lists) and show up in the client report’s totals. Leaving the Agency plan deletes nothing: the site is told to pause, the optimized files and the kept originals stay, and restore keeps working when the plan is back.
What is left alone, and why
A run reports how many images it skipped and why:
- SVG files and GIFs (animated or not), animated PNGs, and WebP or AVIF originals.
- Files over 32 MB, and images too large for the PHP memory limit (decoding needs about 5 bytes per pixel; a 128 MB limit stops around 12 megapixels — the tab shows the limit).
- Files whose contents are not what the extension says (a PNG named
.jpg, a PHP file named.jpg): the real type is read from the bytes, never the name. - Images another optimizer already handled, and images already optimized with the current settings (a hash of the result and the settings it was made with are kept on the attachment).
- Files outside the uploads folder, and missing files.
In client reports
A site with optimized images gets an Images optimized section in its client report — images optimized, what they are lighter by and by what percentage, WebP/AVIF coverage, and what this period’s runs did. It is client-facing on purpose: it is work done for the client. Sites with nothing optimized get no section.
On the public API
On Scale, GET /api/v1/sites/{site}/images (scope sites:read) returns the state, the totals and how copies are served — read-only. Settings, runs and restores stay in the app.
Limits and honest notes
- Quality depends on the host’s image library. Imagick generally produces smaller files than GD at the same quality and keeps colour profiles; GD cannot quantize PNGs, so lossy PNG savings are modest.
- AVIF needs a PHP image library built with AVIF support (GD 2.3.2+ with libavif, or Imagick with a recent ImageMagick). Many shared hosts do not have it yet; the tab says so.
- A run’s speed depends entirely on the host: a few images a second on a VPS, slower on shared hosting, and AVIF encoding is the slow part.
- Only the media library is covered — images a theme or plugin stores elsewhere, and images inlined in CSS, are not touched.
- The kept originals take disk space (about the size of the images before optimization). The tab shows how much the vault holds.