WordPress Image Optimization with EWWW: My Exact Setup and WP-CLI Workflow

How I configure EWWW Image Optimizer on every WordPress site I manage, and why I run all scans through WP-CLI instead of the dashboard.

Barry van Biljon
April 1, 2026
13 min read
WordPress Image Optimization with EWWW: My Exact Setup and WP-CLI Workflow
Back to Blog

Key Takeaways

  • Unoptimized images are the single biggest page weight problem on most WordPress sites, often accounting for 60-80% of total request transfers

  • EWWW Image Optimizer handles compression locally on your server without sending images to a third-party API on the free tier

  • Running bulk optimization through WP-CLI removes the timeout and memory limits that kill dashboard-based scans on large media libraries

  • WebP conversion with EWWW typically reduces image file size by 25-35% over optimized JPEGs with no visible quality loss

  • A scheduled WP-CLI cron job keeps new uploads optimized without anyone touching the dashboard

The problem nobody checks

The single most common performance issue I find has nothing to do with databases, caching, or PHP configuration: it's images.

Did you know: since 5.3, WordPress will scales down images larger than 2560px and applies it's own JPEG compression at quality 82? That's better than nothing, but often results in a low resolution image, which is especially bad for say, a featured product image. WordPress also doesn't convert to WebP, doesn't touch PNGs aggressively, and applies the same light compression to every generated thumbnail.

When you upload a 4MB photo from your phone, WordPress scales it down and generates additional thumbnail sizes, and you end up with 6-8MB of lightly compressed JPEG data from a single upload. None of it in next-gen formats. Multiply that across hundreds of posts and pages, and your images account for 60-80% of total page transfer.

My solution: EWWW Image Optimizer. It's free - and uncomplicated. You need proper lossy compression (not just WordPress's default quality 82), format conversion to WebP, and a process that doesn't depend on someone remembering to manually optimize after every upload.

This is my setup. However, there are alternative solutions, which do the same thing.


Why EWWW Image Optimizer

I've tested most of the image optimization plugins over the years. I keep coming back to EWWW for three reasons:

Local compression with no per-image cost. The free version of EWWW compresses images directly on your server using open-source tools (jpegtran, optipng, pngquant, cwebp). There's no API call, no monthly quota, no "you've used 500 images this month" emails. If you manage 10 sites with 50,000 images combined, the cost is zero.

Automatic optimization on upload. Every image uploaded through the media library gets compressed and converted to WebP automatically. No manual step required. This is true for any image optimizer, but EWWW's implementation is clean and doesn't interfere with other plugins that hook into the upload process.

Full WP-CLI support. This is the real differentiator for me. EWWW exposes its entire bulk optimization engine through WP-CLI, which means I can run scans from the command line without timeout limits, without memory limits, and on a schedule.


My EWWW configuration

Here's exactly how I configure EWWW on a fresh install. I'm covering the settings that actually matter and skipping the ones that are fine at their defaults.

Switch to "Ludicrous Mode" and ensure to enable "Plaid" on the advanced screen. Plaid does absolutely nothing according to the authors, but it's tradition.

Essential settings

Easy IO: DISABLED. This third-party compression server is not necessary and could cause limits, and issues with EWWW in my experience.

Max Dimensions: Keep these at 1920px for width, and leave the height at 0px. If you need photography-quality images, you can bumpt this up to 2560px.

Missing Dimensions: OFF. I have experienced issues with this setting in the past on some websites, but you could experiment with this on your site. If you use something like WP-Rocket's Image Lazyloading it is preferred to set it there.

If the site has no other lazy loading solution, I enable EWWW's lazy loader with the Above the Fold setting configured to skip the first 3-4 images. This prevents the hero image and above-fold content from being lazy loaded, which would hurt LCP (Largest Contentful Paint).

Metadata Removal: OFF. Sometimes, when stripping the color profiles, you lose orientation. This typically saves 5-15KB per image but I leave it OFF. You could switch it ON with caution.

Lossless compression: This is the default mode. It reduces file size without any quality loss by removing redundant data. The savings are modest (10-20%) but risk-free. I start here on every site.

Lossy compression: For sites where page speed matters more than pixel-perfect fidelity (which is most sites), I switch to lossy. The difference between a lossless and lossy JPEG at quality 82 is invisible to the human eye on a website, but the file size difference is significant. EWWW's lossy mode typically achieves 40-60% compression.

WebP conversion

This is the single most impactful setting. Enable WebP Conversion.

EWWW will generate a .webp version of every JPEG and PNG it processes. It only creates the WebP file if it's actually smaller than the original (which it almost always is for JPEGs, and usually is for PNGs). The original file stays untouched.

WebP quality: I set this to 90. The default is 75, which is fine, but 90 gives a slightly better quality-to-size ratio on product images and hero shots. You can set this via a constant in wp-config.php:

define('EWWW_IMAGE_OPTIMIZER_WEBP_QUALITY', 90);

WebP delivery

Generating WebP files is only half the equation. You need to actually serve them to browsers. EWWW offers three delivery methods:

1. Rewrite rules

This only works on Apache / Lightspeed servers. EWWW can insert rules into your .htaccess that serve the .webp version when the browser supports it. This is the fastest option because it's handled at the web server level before PHP even loads.

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_ACCEPT} image/webp
  RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png|gif)$
  RewriteCond %{REQUEST_FILENAME}.webp -f
  RewriteRule (.+)\.(jpe?g|png|gif)$ %{REQUEST_URI}.webp [T=image/webp,L]
</IfModule>
 
<IfModule mod_headers.c>
  <FilesMatch "\.(jpe?g|png|gif)$">
    Header append Vary Accept
  </FilesMatch>
</IfModule>

2. JS WebP rewriting & Picture element rewriting

I usually enable both of these on servers where you can't modify rewrite rules - some managed hosting - and often on Cloud servers running Nginx or Caddy webserver, EWWW injects JavaScript that swaps image sources to .webp on the client side. As a fallback it also wraps images in <picture> elements with <source> tags for WebP. This last one can interfere with some themes and page builders that manipulate image markup.

It works, but it adds a small JS payload and means the browser initially requests the original image before swapping. Not ideal, but better than no WebP at all.

I use the .htaccess rewrite method on every Apache/LiteSpeed server. It's transparent, requires no JavaScript, and has zero performance overhead.

3. Force WebP

Enable. This will force the loading of .webp when sometimes they do not get served on the frontend.

Local settings

Optimization Level

Choose the highest available level for each setting, depending on whether you have the pro version or not. PNG offers premium-level, note.

Backup originals

Enable. Keep this to "Local" since you do not want to re-optimize optimized images over and over.

Advanced settings

Scheduled optimization

If you're going to use WP-CLI on a cron, the safer method, rather leave this OFF. Otherwise keep this ON.

Includes / Excludes

Include the Media Folders, and DO NOT INCLUDE the original version of images - this will lead to very degraded images over time.

You may specify other locations such as:

  • /wp-content/uploads/woocommerce_uploads/ (WooCommerce downloadable product images)
  • /wp-content/uploads/gravity_forms/ (if form submissions include image uploads)
  • Any custom upload directories the theme creates

Running scans with WP-CLI

This is where my workflow diverges from most tutorials. I never run the bulk optimizer through the WordPress dashboard. Here's why:

Timeouts kill large scans. A media library with 3,000+ images will take 20-40 minutes to fully optimize. Most PHP configurations time out at 30-300 seconds. The dashboard bulk optimizer works around this with AJAX batching, but it's fragile. Close the browser tab, lose your WiFi for 30 seconds, or let your laptop sleep, and the scan stops.

Memory limits. PHP's memory limit (typically 256MB-512MB) constrains how many images the dashboard optimizer can process in a single batch. WP-CLI runs from the command line with no memory limit by default.

Scheduling. I want optimization to run automatically at 2am, not when someone remembers to click a button.

The basic command

wp ewwwio optimize media --noprompt

That's it. This scans the Media Library, active theme, and all configured folders, then optimizes every unoptimized image it finds. The --noprompt flag skips the "are you sure?" confirmation, which is essential for automated runs.

Optimizing everything on the site

wp ewwwio optimize all --noprompt

The all flag includes the Media Library, theme folders, and plugin directories. I use media for routine scans and all for the initial optimization after installing EWWW on an existing site.

Adding a delay between images

If you're on shared hosting or a server where CPU spikes will cause problems:

wp ewwwio optimize media 5 --noprompt

The 5 adds a 5-second delay between each image. It takes much longer, but keeps CPU usage manageable. I use this on client sites where I don't control the hosting environment.

Force re-optimization

wp ewwwio optimize media --force --noprompt

The --force flag tells EWWW to re-process images it has already optimized. I use this after changing compression settings (e.g., switching from lossless to lossy) so the new settings apply to existing images, not just new uploads.

WebP-only pass

wp ewwwio optimize media --webp-only --noprompt

If you've just enabled WebP conversion and want to generate .webp files for your existing library without re-compressing the originals, this is the command. It only creates WebP variants and skips the base compression step.

Reset and re-scan

wp ewwwio optimize media --reset --noprompt

The --reset flag clears EWWW's internal tracking of which images have been scanned and starts fresh. Useful after migrations, bulk imports, or if you suspect the tracking data is out of sync with the actual files on disk.

Other useful CLI commands

# Restore images from EWWW backups (if backups are enabled)
wp ewwwio restore
 
# Remove original high-resolution uploads (WordPress 5.3+ creates scaled copies)
wp ewwwio remove_originals
 
# Delete all local WebP copies (if disabling WebP)
wp ewwwio remove_webp
 
# Remove pre-conversion originals after PNG-to-JPG or JPG-to-PNG conversion
wp ewwwio remove_converted_originals

My cron setup

This is what runs on every site I manage:

# Nightly image optimization at 2am server time
0 2 * * * cd /var/www/html && wp ewwwio optimize media --noprompt >> /var/log/ewww-optimize.log 2>&1

Every night at 2am, WP-CLI scans for any unoptimized images (new uploads from that day, regenerated thumbnails, anything that slipped through) and compresses them. The log file gives me a record of what was processed.

For the initial scan on a site with thousands of existing images, I run it manually in a screen or tmux session so it can run for hours without interruption:

screen -S ewww
cd /var/www/html
wp ewwwio optimize all --noprompt
# Ctrl+A, D to detach. Reattach later with: screen -r ewww

What to expect from compression

Real numbers from a WooCommerce store I optimized last month (2,847 product images plus thumbnails):

MetricBeforeAfter
Total image files14,23514,235 (+ 14,235 WebP)
Total image size4.2 GB1.8 GB (originals) + 1.3 GB (WebP)
Average JPEG size312 KB128 KB (original) / 94 KB (WebP)
Average page weight3.8 MB1.4 MB
LCP (mobile)4.2s2.1s

The WebP files served to browsers are roughly 70% smaller than the original unoptimized JPEGs. Even the compressed originals (served as fallback) are 60% smaller.

Page weight dropped by 63%. LCP halved. This was with lossy compression at quality 82 and WebP quality 80. No visible quality difference on any product image.


Common mistakes

Running the dashboard optimizer on 10,000+ images. Use WP-CLI. The dashboard optimizer will time out, lose its place, and you'll end up re-scanning the same images multiple times. If you're not comfortable with WP-CLI basics, get comfortable first, because it's the right tool for this job.

Skipping WebP conversion. Compression alone is good. Compression plus WebP is dramatically better. There's no reason not to enable WebP in 2026. Browser support is effectively universal.

Running two image optimizers. I've seen sites with EWWW and ShortPixel both active, or EWWW and Imagify. They fight over the same images, double-process uploads, and create unpredictable results. Pick one.

Not optimizing theme and plugin images. The Media Library gets all the attention, but your theme's /img/ folder and plugins that store assets outside the media library are often full of unoptimized PNGs. EWWW's folder scanning catches these, but only if you configure it.

Lazy loading above-the-fold images. If your hero image or first product row is lazy loaded, your LCP takes a massive hit. Exclude above-fold images from lazy loading, either through EWWW's Above the Fold setting or with the data-skip-lazy attribute.


When EWWW isn't enough

EWWW handles compression and format conversion, but it doesn't solve every image performance problem:

Serving oversized images. If your theme displays images at 400px wide but the uploaded image is 4000px wide, compression helps, but you're still transferring 10x more pixels than needed. Use WordPress's responsive images (srcset) or manually size images before upload.

Too many images per page. Compression reduces individual file sizes, but if a single page loads 80 product images, it's still a lot of HTTP requests. Lazy loading helps. Pagination helps more.

No CDN. Optimized images served from a single-server origin are still slower than unoptimized images served from a CDN edge node 50ms away from the user. EWWW offers Easy IO as their CDN solution, or you can pair the local optimizer with Cloudflare, BunnyCDN, or any other CDN.


Barry van Biljon

Written by

Barry van Biljon

Connect on LinkedIn

Full-stack developer specializing in high-performance web applications with React, Next.js, and WordPress.

Ready to Get Started?

Have questions about implementing these strategies? Our team is here to help you build high-performance web applications that drive results.

Frequently Asked Questions

Local compression does use server CPU, yes. That's the trade-off for not paying for a cloud API. On a shared hosting plan, running a bulk scan on 5,000 images will spike your CPU usage and could trigger resource limits. This is exactly why I run scans through WP-CLI during off-peak hours instead of through the dashboard during business hours. If server resources are genuinely tight, EWWW offers a cloud-based Easy IO CDN that offloads the compression entirely.