I Shaved 156 MB Off My Blog by Converting PNGs to WebP

A factory conveyor belt where oversized PNG cubes pass through a compression machine and emerge as tiny polished WebP gems, monitored by a woman engineer

I just shaved 156 MB off my blog. Not the database, not the theme, not some plugin audit. The images.

The Problem Nobody Talks About

If you use AI to generate featured images for your blog posts, those PNGs are probably massive. I never thought to check mine. Then I did.

139 featured images. All PNG. Average size: 2.7 MB. The largest: 8.3 MB. Total media weight: roughly 168 MB of featured images alone.

Every visitor to any post was downloading 1.5-8 MB just for the hero image before reading a single word. WordPress generates six derivative sizes per upload, so my media library was full of enormous PNG thumbnails that nobody asked for.

AI image generators (Gemini, DALL-E, Midjourney) output PNG by default because it’s lossless. That’s great for archival. It’s terrible for web delivery.

WebP: The Fix

WebP is Google’s image format, and it’s been supported by every major browser since Safari 16 in late 2022. Global browser support is north of 97%. If your visitors are using a browser that can’t render WebP, they have bigger problems than your blog images.

The pitch: lossy WebP at quality 90 produces images visually indistinguishable from the PNG original, at a fraction of the file size.

How much of a fraction? Here’s what I measured on my actual images:

Image Type PNG Size WebP Q90 Savings
1536×1024 (typical AI) 1,880 KB 138 KB 91%
1734×907 (wide format) 2,378 KB 196 KB 92%
1424×752 (flat color areas) 8,308 KB 105 KB 99%

That’s not a typo. An 8.3 MB image became 105 KB. AI-generated images compress extraordinarily well in WebP because they have large regions of smooth color gradients – exactly the kind of content WebP’s predictive coding eats for breakfast.

The Non-Obvious Blocker: IIS

I run WordPress on Windows with IIS (yes, really – it works great, and I’ll fight anyone who says otherwise). The first thing I discovered is that WordPress will happily accept a WebP upload via the REST API, report success, generate all the derivative sizes, and then… serve a 404 when you try to load the image.

IIS doesn’t know what a .webp file is unless you tell it. Add this to your web.config inside the <system.webServer> section:

The remove before add is defensive – it prevents a “duplicate entry” error if the MIME type already exists at the machine level.

If you’re on Apache or Nginx, you probably don’t hit this. If you’re on IIS, this will cost you an hour of debugging before you think to check MIME types.

The Tool: cwebp

Google publishes a standalone WebP encoder called cwebp. No installer, no dependencies – just download the zip, extract, and run.

Download: libwebp 1.5.0 for Windows x64

Basic usage:

Quality 90 is the sweet spot. I tested Q70 through Q95 on several images. Below Q85, you start seeing compression artifacts in gradients. Q90 to Q95 is visually identical, but Q90 is 20-30% smaller.

Automating the Bulk Conversion

I had 139 images to convert, and each one needed six steps:

  1. Download the PNG from WordPress
  2. Convert to WebP with cwebp
  3. Upload the WebP back to WordPress
  4. Back up the post’s HTML content (just in case)
  5. Update the post’s featured_media to point to the new image
  6. Find and replace any inline &lt;img&gt; references in the post body

Here’s the PowerShell script that does it all via the WordPress REST API:

Run it in batches:

Key Design Decisions

Quality 90, not 80. Most WebP guides recommend Q80. I went with Q90 because these are hero images displayed prominently at 768px wide. The difference is ~30 KB per image but noticeably sharper on high-DPI screens. When your total savings are measured in megabytes, an extra 30 KB for visual quality is a trade worth making.

Skip when WebP is larger. One of my 139 images was a tiny 17 KB screenshot. WebP encoded it at 40 KB – the overhead of the WebP container exceeded the savings. The script detects this and leaves the original PNG in place. Don’t blindly convert everything.

Keep the originals. The old PNG media items stay in WordPress. They cost disk space and nothing else. If something goes wrong with a WebP image six months from now, the rollback CSV tells me exactly which PNG to restore.

Back up before modifying. Every post’s raw HTML is saved to disk before the script touches it. Belt and suspenders. The rollback CSV maps old media IDs to new ones, so reverting a single post is a one-liner.

Use curl for uploads. PowerShell’s Invoke-RestMethod doesn’t handle binary file uploads to the WordPress media endpoint reliably. curl.exe with --data-binary works perfectly every time.

The Results

Metric Before After
Total featured images 139 PNG 138 WebP + 1 PNG
Total media weight ~168 MB ~12 MB
Average image size 2.7 MB ~100 KB
Per-page hero image 1.5-8 MB 60-400 KB
Best compression 99% (8.3 MB to 105 KB)
Worst meaningful 63% (complex screenshot)
Conversion failures 1 (timeout, succeeded on retry)

The entire conversion took about 90 minutes, processing 10 images per batch with visual verification between batches.

What I’d Do Differently

Convert before uploading. If I were starting a new blog today, I’d convert every AI-generated image to WebP before uploading it to WordPress. The cwebp command takes less than a second. There’s no reason to upload a 3 MB PNG when a 150 KB WebP is visually identical.

Automate the IIS check. I spent an embarrassing amount of time debugging why uploaded images returned 404 before realizing IIS didn’t have the WebP MIME type. A quick PowerShell check at the top of the script would have caught it:

Try It Yourself

If your WordPress blog uses AI-generated featured images, you’re probably sitting on hundreds of megabytes of unnecessary PNG weight. The conversion is safe (rollback data is preserved), fast (minutes, not hours), and the page speed improvement is dramatic.

Your visitors’ browsers will thank you. Your hosting provider’s bandwidth bill will thank you. And your Lighthouse score will stop yelling at you about “properly size images.”

Questions? Found a bug in the script? Let me know on Bluesky or LinkedIn.