Two weeks ago I described how this blog is built and delivered. Since then, I added some more goodies, so here is part two of how this blog works.

Tag cloud

I found a Jekyll plugin (jekyll-tagging) to produce the tag cloud in the right column. It processes the tags in the front matter of all posts, generates an index page for each tag and displays links to them in a font size relative to their usage. It works well, but not perfectly:

  • I don’t want to show tags with too little usage.
  • Choosing a font size linear to the usage of a tag tends to result in many small entries and a few very big entries. I’d like a logarithmic function.

So I wrote my first useful thing in Ruby and implemented these features. After more than ten years with Java, it was an interesting experience. On one side there is simple syntax, nice library functions and it’s easy to start with. On the other hand, it’s difficult to know what type an object has and what methods and properties it supports. And I don’t like the navigation on http://www.ruby-doc.org/.

Image optimization

Hosting a site on Amazon CloudFront should result in a good performance. So I tested it with Google PageSpeed. The results were not very good, due to the images being way too big. I resized them to 800px width. PageSpeed was happy. At least for desktop users. For mobile, this is still far from optimal.

After some research, I used my new ruby knowledge and wrote a Jekyll plugin to solve the problem. It generates scaled versions of all images and provides a liquid tag to access them. A post such as

---
title:  Images
image:
    -   url: sweden88.jpg
        caption: Bier I
---
{% image 0 %}

would render HTML as

<figure>
    <picture>
        <img srcset="450x450max/sweden88.jpg 450w,
                     600x600max/sweden88.jpg 600w,
                     800x800max/sweden88.jpg 800w"
             sizes="(min-width: 1000px) 800px,
                    calc(100vw - 60px)" />
        <figcaption>Bier I</figcaption>
    </picture>
</figure>

It uses srcset to define the image in different resolutions and let the browser decide which is the best to use. sizes tells the browser something about the layout:

  • If the browser window has a width of 1000px or more, the image will be scaled to 800px.
  • In smaller browser windows, the image has the width of the window minus 60px.

Now PageSpeed was happy with my images. Almost.

Caching

It told me to deliver them with an expires HTTP header. This tells the browser how long it is allowed to cache the image. As I want to change my posts (and images) after being published, I cannot set the expiration date far in the future.

But one can add a hash code to the file name of a resource. Thus, when a resource changes, it’s effectively a new resource with another name and the browser does not find it in the cache. Therefore, such tagged resources can be delivered with expiration dates far in the future because they never expire.

Preferring immutability has many advantages.

So I adjusted my image plugin to support file names with hashes. I also adjusted my PHP script used to deploy the blog. If a file named .s3 exists in the root folder, it is used to set options of the AWS CLI tool. My .s3 file looks like this:

fotos/* = expires:360d,size-only,delete
assets/* = expires:360d,delete
* = expires:1d,delete

Meaning that

  • Photos should be delivered with an expiration date 360 days from now, they should only be uploaded to S3 if their size has changed and deleted files should also be deleted on S3.
  • Other assets should also be delivered with an expiration date 360 days from now.
  • All other files (e.g. posts itself) expire after one day.

Compression

Another measure to increase the performance of a website is compression. Sending less data over the internet simply takes less time. If the browser sends a Accept-Encoding: gzip header, the server is safe to send compressed data. Amazon CloudFront supports these headers in that it forwards them to its backend. This is in my case an S3 bucket which unfortunately does NOT process the header. So I’m stuck with these options:

  • Use another data store instead of S3.
  • Send always compressed data, as all modern browser support it.
  • Don’t do anything and send uncompressed data.

I decided to do nothing for the moment and just be happy with the achieved speed. But who knows, maybe sometime there will be a part III of how this blog works.