Over the past week and a half, I've been thinking a lot about responsive image handling. Before migrating this blog to Hugo late last year, I never had to think much about responsive images becaues WordPress took care of all of that. Hugo doesn't make any assumptions, which is both a good and bad thing. On one side, building a custom responsive image handling system in Hugo allows for a lot of flexibility, and personally speaking, I love working with Hugo's templating system. On the other hand, the lack of an “off the shelf” solution like WordPress’ responsive image handling often leave me with a sense of “am I doing this right” – mostly because my development skills are still intermediate on a good day.

I ignored responsive images for a while because I thought I didn't need them. For most of 2018 and 2019, my posts were text-heavy with an occasional sprinkling of images. Now that my life is in a more stable place, I've been wanting to get back into photography and share my photos. With that in mind, I spent last weekend building a four-image gallery shortcode that looks like this.

At full-width, each image in the gallery is 720 pixels wide. I process and upload my images with a width of 1920 pixels. It doesn't make sense to serve such a large image when the maximum pixel size required is 1440 (720 x 2) pixels for 2x DPI displays. I intially thought about generating various thumbnail sizes for all my images, but then I rememebered that managing image sizes was one of the things I hated most about WordPress. Instead, I decided to put my Cloudflare business plan to its full potential and enabled the “image resizing” feature.

Image resizing analytics in the Cloudflare dashboard.
Image resizing analytics in the Cloudflare dashboard.

Cloudflare image resizing works by prepending an endpoint to your images. Here are two URLs – a normal image URL, and a Cloudflare image resizing URL.

/* Normal URL */
https://cdn.brianli.com/uploads/2020/01/20200113_MT-FUJI-IN-THE-DISTANCE.jpg

/* Cloudflare Image Resizing URL */
https://brianli.com/cdn-cgi/image/fit=contain,format=auto,metadata=none,onerror=redirect,quality=70,width=1920/https://cdn.brianli.com/uploads/2020/01/20200113_MT-FUJI-IN-THE-DISTANCE.jpg

As you can see, the Cloudflare URL is made up of 4 parts.

  1. Cloudflare Zone - https://brianli.com
  2. Image Resizing Endpoint - /cdn/image
  3. Image Resizing Sedttings - /fit=contain…width=1920
  4. Original Image URL - https://cdn.brianli.com…jpg

Implementing Cloudflare Image Resizing in Hugo

First, I turned on the image resizing feature in the Cloudflare dashboard. I left “resize images from any origin” unchecked because I only need image resizing on two domains (brianli.com and cdn.brianli.com) – note that subdomains do not count as a non-origin domain. If I understand correctly, turning on the “any origin” feature would allow anyone to use your image resizing endpoint to process images – not a good idea since image resizing is a billable feature.

To add Cloudflare image resizing to my Hugo site, I had to make some changes to my image shortcode. Specifically, I had to add the srcset HTML attribute to define different sizes for my images. First, I made a Hugo partial to store my Cloudflare image resizing settings. For more information on the various parameteres (fit, format, etc.), please refer to Cloudflare's official documentation.

/* Cloudflare Image Resizing Settings */
/cdn-cgi/image/fit=contain,format=auto,metadata=none,onerror=redirect,quality=70,

Next I added the following srcset to my image shortcode template. For each image size, it calls images/cloudflare/cf_image_resizing_settings.html, and appends the Cloudflare width setting, image file path, and image width.

srcset="
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1400/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1400w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1366/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1366w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1280/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1280w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1200/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1200w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1024/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1024w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=1000/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 1000w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=800/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 800w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=768/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 768w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=600/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 600w,
{{ partial "images/cloudflare/cf_image_resizing_settings.html" . }}width=400/{{ .Site.Params.cdnurl }}uploads/{{ .Get "src" }} 400w
"

Cloudflare Image Resizing in Practice

After integrating the srcset attribute, I was eager to see if I had set everything up correctly. I pushed my site to live, and proceeded to do some testing with various devices. Surprisingly enough, everything worked without any additional tweaks. For example, a test image on desktop resolves to this URL (1920 px), while viewing the same image on my iPhone X resolved to this URL (1000 px).

The comparison chart below shows the performance benefits of Cloudflare image resizing. The initial size of the 1920 px test image was 348 KB. With Cloudflare image resizing, the file size dropped to 220 KB. Lastly, since I specified format-auto in my image resizing settings, WEBP-compatible browsers like Google Chrome and Brave were served an image weighing in at 135 KB.

A comparison chart showing the performance benefits of Cloudflare image resizing.

For this particular test, Cloudflare didn't resize the dimensions of the image, but still managed to reduce the file size dramatically by stripping metadata and reducing quality to 70 for JPEGs. The WEBP compression, which resulted in a 61% decrease in file size was especially impressive. I'd expect to see an even bigger difference on mobile devices where resizing of the physical image dimensions would come into play as well.

Conclusion

Without a doubt, I can say that Cloudflare image resizing is a great product if you're looking for fast and reliable edge image processing. I'm not sure if I'll keep Cloudflare image resizing over the long term – mainly because I may move away from my business plan in the future. With that said, I'm now a huge proponent of image processing at the edge. For my use case, it makes sense because I hate the storage bloat of maintaining multiple image sizes on-server. In the future, I may take a look at other similar products like Cloudinary and Netlify image transformation. For now, I am very happy with Cloudflare image resizing and the performance improvements it brings to my blog.