Self-hosting all my web assets

I’m really really not a front end engineer (at best I can be called an enthusiastic amateur, but that still feels like an overstatement of my abilities), but I do have quite a strong interest in how the web works.

Related to that, I wanted to get this website serving all its assets from my domain; previously it served assets from a variety of domains including:

  • https://use.fontawesome.com
  • https://fonts.googleapis.com
  • https://cdnjs.cloudflare.com

These came with the Hugo Theme I am using (Coder), providing styling (CSS) and fonts to make the website look pretty.

I’m generally a fan of the idea of self-hosting these kinds of assets for a few reasons:

  1. It makes your website self-sufficient. Your website will always render correctly unless your host is down (unlikely as it is that my site will stay up while Cloudflare or Google goes down)
  2. You can more easily control what version of assets gets served
  3. You can avoid leaking personal information about your viewers to 3rd parties
  4. I think your website should load faster, because less TLS connections will need to be opened (plus a bunch of other reasons given by technical blogs I link to below)

Getting started

I saw a post that rose to the top of Hacker News about this that caught my eye. There’s a lot of interesting information in the post (including how clever Google Fonts is about how it serves the actual font assets), but conclusion took me on an emotional rollercoaster (emphasis mine).

Excitement:

To answer the question in the title of this post: yes it’s better to self-host as the performance gains are substantial.

Pause for thought:

Of course your mileage may vary as it will depend on your exact site so test, test, test, but I would imagine it would be the better option, from a performance perspective, for most sites.

Dissapointment:

However, Google Fonts is not just a repository of hundreds of free fonts - it is also a clever delivery mechanism utilising many of the latest web performance techniques to try to deliver the most appropriate fonts, with the minimal effort to the website owner. In moving to self-hosting you ideally want to re-implement as many of those improvements like font-display: swap, subsetting, and removal of hinting (at least for some browsers), or you may actually negatively impact performance by having larger fonts files.

Making it happen

I decided to do it anyway; my vistor count is low (I think approximately zero, but I don’t actually have any analytics to back that up) and I doubt performance changes (better or worse) will change that, and my reasons for being interested in this aren’t performance related anyway.

Unfortunately, I didn’t document the steps I took, so this is just a series of recollections; things could be missing.

First, I had to collect the assets that were being downloaded. I needed to collect:

  • 3 fonts (2 variations of each) from Google Fonts
  • all.css from Font Awesome
  • normalize.css, a commonly used web component that was being hosted by CloudFlare
Fonts

Using the google-webfonts-helper, I could find downloadable packs of the 3 fonts used by the Coder theme - Lato, Merriweather, and Source Code.

The tool lets also choose whether you want to download fewer assets and only support ‘modern browsers’, or a larger pack that has ‘best support’. I opted for ‘modern browsers’, so only downloaded the .woff and .woff2 files.

I also checked their license using Google’s font attribution list to make sure I wasn’t breaching them; all 3 fonts have the permissive SIL Open Font License, 1.1 license.

Font Awesome’s fonts and CSS

This was pretty easy once I found Font Awesome’s guide to Hosting Font Awesome Yourself.

I included Font Awesome’s webfonts in the ./static/webfonts directory which seemed to work.

Normalize.css

This was also easy - its available to download from its offical website

Replacing the 3rd party requests in the Coder theme

Now that I had the assets, I needed to make the site pull them in locally, instead of requesting them from the various websites that host them.

In my case, the 3rd party requests were included in the ./themes/hugo-coder/layouts/_default/baseof.html as below:

24
25
26
<link href="https://fonts.googleapis.com/css?family=Lato:400,700%7CMerriweather:300,700%7CSource+Code+Pro:400,700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" integrity="sha384-KA6wR/X5RY4zFAHpv/CnoG2UW1uogYfdnP67Uv7eULvTveboZJg0qUpmJZb5VqzN" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" integrity="sha256-l85OmPOjvil/SOvVt3HnSSjzF1TUMyT9eV0c2BzEGzU=" crossorigin="anonymous" />

To overwrite files provided by your theme in Hugo, you put an identically located file in your main directory - so I needed to put a modified version of the file in ./layouts/_default/baseof.html.

I originally just replaced the 3 lines above with the 3 lines below, having placed the css files in the ./static/ folder:

24
25
26
<link rel="stylesheet" href="/custom.css"/>
<link rel="stylesheet" href="/all.css"/>
<link rel="stylesheet" href="/normalize.css"/>

custom.css on Line 24 refers to the following file I created, which points the browser at the various .woff/.woff2 files based on what font is needed:

/* merriweather-300 - latin */
@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 300;
  src: local('Merriweather Light'), local('Merriweather-Light'),
       url('fonts/merriweather/merriweather-v21-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
       url('fonts/merriweather/merriweather-v21-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
}
/*  */
/* continued below for each of my fonts */
/*  */

This was great because now all my assets were hosted on the same domain, but pretty sub optimal because loading them involved 3 separate requests that loaded unneccisarily large files.

Making them load faster

Ideally, these assets would be transferred in one request, as they’re always requested by the same page. I could have copy-pasted them into one file, but that would have made them confusing to maintain. Ideally, they’d also be minified to reduce the total data transmitted.

Luckily, Hugo has the concept of pipes to help out with situations like these. This is basically taking files and passing them to functions downstream (similar to how UNIX pipes work, I guess).

I moved the css files to the ./assets folder, as that is the default location Hugo’s pipes work from.

(It turns out I’d left Font Awesome’s webfonts in the ./static/ folder but nothing seems to have broken; I’m still actually a little confused about how to use ./assets vs ./static in the root of my Hugo project.)

The new ./layouts/_default/baseof.html file looked like:

24
25
26
27
28
29
30
31
{{ $custom := resources.Get "custom.css" | minify }}

{{ $all := resources.Get "all.css" | minify }}

{{ $normalize := resources.Get "normalize.css" | minify }}

{{ $allcss := slice $all $custom $normalize | resources.Concat "bundle.css" | fingerprint }}
<link rel="stylesheet" href="{{ $allcss.Permalink }}" integrity="{{ $allcss.Data.Integrity }}">

Lines 24, 26, and 28 are Hugo directives that take the css files and pass them to Hugo’s minifier, saving them with a resource name.

Then on line 30, I create a new resource that bundles all 3 together and generates a fingerprint (for integrity checking when the browser downloads them).

Then, line 31 is the html that actually makes the stylesheet request, retrieving the single, minified css file!

Worth it?

I think I’ve managed to self-host my assets in a pretty sub-optimal way, but I’m happy with the result and don’t really want to spend more time on it.

Ideally I:

  1. Would not be copy-pasting Coder’s baseof.html verbatim into my project, and then editing only 3 lines from it
  2. Have some automated build process for collecting the various assets and putting them in the right folders to make updating versions easier

But I feel like I’ve learned a bit about Hugo, css, webfonts, and Google’s PageSpeed Insights gives my website a score of 100!