Implementing Netlify’s Image CDN

tl;dr I implemented Netlify’s new image transformation service on my icon gallery sites and saw a pretty drastic decrease in overall bandwidth. Here are the numbers:

Page Requests Old New Difference
Home 60 1.3MB 293kB â–Ľ 78% (1.01MB)
Colors 84 1.4MB 371kB â–Ľ 74% (1.04MB)
Designers 131 5.6MB 914kB â–Ľ 84% (4.71MB)
Developers 140 2.5MB 905kB â–Ľ 65% (1.62MB)
Categories 140 2.2MB 599kB â–Ľ 73% (1.62MB)
Years 98 4.7MB 580kB â–Ľ 88% (4.13MB)
Apps 84 5.2MB 687kB â–Ľ 87% (4.53MB)

For more details on the whole affair, read on.

A Quick History of Me, Netlify, and Images

This post has been a long time coming. Here’s a historical recap:

Phew.

Ok, so now let’s get into the details of implementing Netlify’s image CDN.

How It Works

The gist of the feature is simple: any image you want transformed, just point it at a Netlify-specific URL and their image service will take care of the rest.

For example: instead of doing this:

<img src="/assets/images/my-image.png">

Do this:

<img src="/.netlify/images?url=/assets/images/my-image.png">

And Netlify’s image service will takeover. It looks at the headers of the browser making the request and will serve a better, modern format if supported. Additionally, you can supply a bunch of parameters to exercise even greater control over how the image gets transformed (such as size, format, and quality).

How I Use It

Given my unique setup for delivering images, I spent a bit of time thinking about how I wanted to implement this feature.

Eventually I settled on an implemntation I’m really happy about. I use Netlify’s image CDN in combination with their redirects to serve the images. Why do I love this? Because if something breaks, my images continue to work. It’s kind of like a progressive enhancement use of the feature.

Previously, I had multiple sizes for each of my icons, so paths to the images looked like this:

<img src="/ios/512/my-icon.png">
<img src="/ios/256/my-icon.png">
<img src="/ios/128/my-icon.png">

Using Netlify’s redirects rules, I kept the same URLs but added a single query param:

<img src="/ios/512/my-icon.png?resize=true">
<img src="/ios/256/my-icon.png?resize=true">
<img src="/ios/128/my-icon.png?resize=true">

Now, instead of serving the original PNG, Netlify looks at the size in the URL path, resizes the image, and converts it to a modern format for supported browsers.

There’s more going on here as to why I chose this particular setup, but explaining it all would require a whole different blog post. Suffice it to say: I’m really happy about how this new image CDN feature composes with other features on Netlify (like the redirects engine) because it gives me tons of flexibility to implement this solution in a way that best suites the peculiarities of your project.

How It Turned Out

To test out how much bandwidth this feature would save me, I created a PR that implemented my changes. It was basically two lines of code.

From there, Netlify created a preview deploy where I could test the changes. I put the new preview deploy up side-by-side against what I had in production. The differences were pretty drastic.

For example, the site’s home page has 60 images on it, each displayed at 256px if you’re on a retina screen. It resulted in a 78% drop in bandwidth.

Additionally, the index pages for icon metadata (such as the designers page) can have up to 140 image on them. On a retina screen, 60 of those are 256px and 80 are 128px. They also a huge reduction in overall bandwidth.

A side-by-side screenshot of the designers index page for iOS Icon Gallery. On the left is the “old” page and on the right is the “new” page. Both websites look the same, but both also have the developer tools open and show a drastic drop in overall resources loaded.

Here’s the raw data showing the difference in overall resources loaded across different pages of the old and new sites (the old serving the original PNGs, the new serving AVIFs).

Page Requests Old New Difference
Home 60 1.3MB 293kB â–Ľ 78% (1.01MB)
Colors 84 1.4MB 371kB â–Ľ 74% (1.04MB)
Designers 131 5.6MB 914kB â–Ľ 84% (4.71MB)
Developers 140 2.5MB 905kB â–Ľ 65% (1.62MB)
Categories 140 2.2MB 599kB â–Ľ 73% (1.62MB)
Years 98 4.7MB 580kB â–Ľ 88% (4.13MB)
Apps 84 5.2MB 687kB â–Ľ 87% (4.53MB)

Out of curiosity, I wanted to see what icon in my collection had the largest file size (at its biggest resolution). It was a ridiculous 5.3MB PNG.

Screenshot of macos finder showing a list of PNG files sorted by size, the largest one being 5.3MB.

Really I should’ve spent time optimizing these images I had stored. But now with Netlify’s image service I don’t have to worry about that. In this case, I saw the image I was serving for that individual icon’s URL go from 5.3MB to 161kB. A YUGE savings (and no discernible image quality loss — AVIF is really nice).

When something is “on fire” in tech, that’s usually a bad thing — e.g. “prod is on fire” means “all hands on deck, there’s a problem in production” — but when I say Netlify’s new image CDN is on fire, I mean it in the positive, NBA Jam kind of way.