One toggle cut the image bandwidth our server bills us for by 73%. Here are the before and after numbers, measured on this site.

If your host meters bandwidth (Kinsta does, per site, and most managed hosts have a line for it somewhere on the bill), images are almost always the part you can do something about. Not because they are the largest share, but because they are the share that does not need to come from your server at all.

What “bandwidth” means on the bill

Every byte your server sends counts: the HTML of each page, the CSS and JavaScript, and every image a browser or a crawler fetches. A page is a few dozen kilobytes. A photo is a few hundred, and a full-size original can be several megabytes. So the moment a visitor opens a gallery, clicks a lightbox, or a share card fetches your og:image, the server pays in bandwidth what it never paid in page views.

On 3 September, before any change, meowapps.com sent 2.25 GB from the server in a day. A third of it, 0.75 GB, was images, and 0.52 GB of that was full-size originals.

Where the images really come from

Diagram: lightbox clicks, og:image and plain src, and Google Images all fetch from your server; after the change every image URL points at the CDN

Most CDN setups for WordPress rewrite the srcset, the list of sizes a browser picks from, and stop there. That covers the image a visitor sees in the article. It misses three things, and those three were most of our 0.52 GB:

  • The lightbox click. When someone opens an image full-screen, the lightbox loads the original file, and that link is built from the attachment URL, not the srcset. One click, one multi-megabyte download from the server.
  • The plain src and the og:image. Browsers use srcset, but crawlers, share cards, RSS readers and Google Images read the plain src and the og:image tag. Those still pointed at the server.
  • Google Images. Once your originals are indexed, Googlebot-Image and everyone who clicks through in an image search fetch the original, from wherever the src says.

The one toggle

Perfect Images has an Easy IO option. Easy IO is the image CDN from the EWWW team: it fetches each image from your server once, then serves it from its own edge, resized and converted to the lightest format the visitor’s browser accepts. Turn it on and Perfect Images rewrites the srcset. Then make sure every image URL goes through it: the plain src, the og:image and the lightbox’s full-size link. That last part is what closes the three leaks above, and it is the difference between a nice improvement and the numbers below.

Two things to keep in mind. Only images should go through an image CDN: if your site sells downloads, the zips stay on your server. And if you use a lightbox that caches image URLs (Meow Lightbox keeps them for 24 hours), clear its cache after the change, or the old links live on for a day.

Before and after, measured on this site

Both days are full days read from the server’s own access log, so they only count what actually left the server. 7 September is the fair comparison: its HTML traffic, which the change does not touch, was within 10% of the baseline day.

Before (3 Sept)After (7 Sept)
Total sent by the server2.25 GB1.49 GB−34%
HTML (untouched, the control)1.42 GB1.28 GB−10%
Images sent by the server0.75 GB0.20 GB−73%
Images reaching visitors from the server0.75 GB0.11 GB−86%
Full-size originals to visitors, per hour21.6 MB3.1 MB−86%
Image bytes per GB of HTML0.530.16−70%

The difference between the two image rows is the CDN itself: Easy IO fetches each new image and each size once, and that first fetch is still server bandwidth. On a quiet day it is about 50 MB; on a day we publish new images, more. It is one-time per file and it never reaches a visitor.

The same setup on Offbeat Japan, a photo-heavy site that had just crossed 67 GB on a 65 GB plan with images at 93% of its traffic, cut origin image bytes by 61% within the first four hours. Two sites, two very different mixes, the same lever.

The honest part

After the change, 85% of what our server sends is HTML. An image CDN does nothing for that, and neither does anything else on the image side. If the bandwidth line on your bill is still too high once the images are gone, the next lever is page caching, ideally at the edge, so most visitors never reach the server at all. And about 25 MB a day of originals still come from the server no matter what, fetched by Googlebot-Image and other crawlers that follow whatever URL they indexed months ago. That is the cost of being in Google Images, and it is worth paying.

How to do it

  1. Look at what you are sending. Your host’s analytics show bandwidth per day; the access log shows what it was. If images are a third or more of it, keep reading.
  2. Install Perfect Images, open its settings and enable Easy IO with your Easy IO domain. Make sure the option that rewrites every image URL is on, not only the srcset.
  3. Open an article in Chrome, press F12, filter the Network tab by Img, and reload. Every image should load from the CDN domain. Click a lightbox: the full-size file should too. Check the og:image in the page source.
  4. If you use a lightbox or a gallery plugin with its own cache, clear it once. Then wait a day and read the log again.

While you are there, look at the largest originals in your log. Ours were hand-drawn PNG sketches saved at 1536 pixels, ten megabytes a day each. A JPEG at quality 88 is a fifth of the size and nobody can tell. The CDN would have served them anyway, but there is no reason to make it work that hard 🙂