I was looking into how Caddy serves pre-compressed files (html/css/js), it’s pretty straightforward, I just need to generate the compressed files after Hugo builds the site. However, that is small fraction of the total payload since a lot of my posts have images. So I’m tackling that first.

So why this and why now? I will eventually be moving this blog/site to own VPS and serve it via Caddy. I have really enjoyed the convenience of Sourcehut Pages and being able to utilize their build system for CI/CD, but I would like to do so with Forgejo instance fully on my VPS. Still, why? Why not, it’s all for fun and learning.

The previous post seemed like a good one to optimize since the payload was actually quite hefty at 5.67 MB (mostly due to the three images). While this is not “bad” by modern web standards, it is non-value-add heavy. The images were inserted as figure and even though the max-width was 800px, it is loading the full size image. I’m a lot more mindful of this in the Film section of this site since it is all images but I had not taken the time to optimize the image experience for regular posts.

Showing 5.67 MB transferred
Pre Optimization

So how much bandwidth is saved on a cold-cache load? 12x! That is more than an order of magnitude savings for an arguably better experience (now the images can be clicked to open the original). 12x!!

Showing 449.66 kB transferred
Post Optimization

It’s a real shame because it didn’t take long and I already had most of the shortcode from when I implemented the exif display for photos. I added a new shortcode fig in /layouts/shortcodes/fig.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{{ $srcUrl := .Get "src" | safeURL }}
{{ $originalImage := .Page.Resources.GetMatch (printf "%s" $srcUrl) }}
{{- $alt := .Get "alt" -}}
{{- $title := .Get "title" -}}
{{- $width := default "600px" (.Get "width") -}}

{{- $resize := default "800x" (.Get "resize") -}}

{{- /* generate thumbnail */ -}}
{{ .Scratch.Set "image" ($originalImage.Resize $resize) }}
{{ $resizedImage := .Scratch.Get "image" }}

<figure class="align-center">
      <a href="{{ $originalImage.RelPermalink }}"> <img loading="lazy" style="display: block; margin-left:auto; margin-right:auto;" src="{{ $resizedImage.RelPermalink }}" alt="{{ $alt }}" width="{{ $width }}" /></a>
      <figcaption>{{ $title }}</figcaption>
</figure>

note: width and resize are optional so I can easily control the viewing experience.

And updated my Hugo Image Yasnippet template from using the HTML default figure to my custom fig. So now whenever I insert an image, I don’t do anything differently, the shortcode takes care of it all.

1
{{ < fig src="${1:SOURCE}" width= "${2:6}00px" title="${3:TITLE}" alt="${4:ALT-TEXT}" align="center" >}}

note: there’s a space between {{ and < when Hugo templating should not have that, I can’t get Hugo to not render that even though it’s in a code fence. I’ll take any advice anyone has. Yes, it’s a Hugo thing, not a ox-hugo export issue, I tried with the generated Markdown file as well.

Oh, and how much would compressing the HTML save? 3.5x, between raw html and serving zstd which is what Sourcehut serves by default. But the difference there is 35 kB vs 10 kB. My plan now is still to pre-compress the text files after the Hugo build process and configure Caddy to offer zstd (and br?) but also eventually go back and update all images to use this new fig shortcode.