Font optimization with Glyphhanger

Photo by Natalia Y on Unsplash

Font optimization with Glyphhanger

As web-vitals and site performance are on it's way to an SEO factor I've checked my website if there's anything I can optimize. After checking current performance the biggest part of my website were web fonts - I use 3 fonts in multiple font weights on my website. Before optimization this resulted in 10 requests summing up to 1MB traffic.

So I started my search on how to optimize this. I already use Cloudflare CDN with aggressive caching in browser and edge. The next was checking if I can reduce the number of requests. Yes, I was able to drop some font weights used only once. Next idea, try to reduce the file size - that's what this article is about as for the other step there are enough tutorials out there.

Subsetting

One way to reduce the file size of a font is to subset it - this means to remove character definitions from the file. You can think about it as PurgeCSS for fonts.

Google Fonts has two ways to do this. Select subsets like latin, greek, cyrillic or provide a list of characters you need.

As I'm not using Google Fonts this wasn't a real option - but if they can do it on runtime there must be any kind of script/tool to do this.

Glyphhanger

The solution I've found is filamentgroup/glyphhanger which provides an impressive CLI API. Right after installing it I got my first errors as a python library is missing and because of some clang stuff on MacOS Big Sur also unable to compile.

So next try, check if there's a docker image that will allow me to use it. For sure there is one Worie/docker-glyphhanger) which also provides a bash alias to use it as if it's installed locally. The alias is glyphy every argument stays the same as with the native one - my examples will use the alias.

For me I had two fonts I was able to subset. The font used in my logo/name Permanent Marker as it has a finite list of characters that will "never" change. And my default body font Inter which I could subset to latin as It's very unlikely that I will write something in cyrillic. Except for those two options glyphhanger provides a lot more functionality - which I didn't need but you can read in the documentation.

Character list

First my logo font - you can use the --whitelist option to provide a list of characters you want to keep. In my case that's TomWitkowski.

bash
glyphy --whitelist=TomWitkowski --subset=fonts/permanent-marker-400.woff --formats=woff

This command will create a new file only containing my name in woff format at fonts/permanent-marker-400-subset.woff.

One cool thing about @font-face in CSS is the unicode-range property which allows you to define the characters this font contains. So this font will never get loaded if there aren't at least one of the listed characters on the page.

The CSS for my new font is as follows:

css
@font-face {
  font-family: "Permanent Marker";
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  unicode-range: U+54, U+57, U+69, U+6B, U+6D, U+6F, U+73, U+74, U+77;
  src:
    local("Permanent Marker Regular"),
    local("Permanent Marker-Regular"),
    url("../fonts/permanent-marker-400-subset.woff") format("woff");
}

As you can see - nothing really special except the unicode-range - don't worry, glyphhanger will output the unicode-range so you can copy it.

Character set

The second option is to subset by one of the predefined character sets - in my case it will be latin. To do so glyphhanger provides shorthand options.

bash
glyphy --LATIN --subset=fonts/inter-*.woff --formats=woff

This command will process all woff files starting with inter- in the fonts directory and only keep the latin character set.

The CSS for this will be the same as above but with a different unicode-range you can copy again from the glyphhanger output.

Licenses

@stauffermatt mentioned that not every license allows you to modify the files. As subsetting removes characters from the font it's a font modification - so check your license before doing it.

Caching

This is another point you should keep in mind while selecting your list of characters. One of the best ways to optimize the performance of your site is caching on the client-side. So if you change the used font files on every page or after every deployment the user has to download a new file. So you should choose a set that will contain all characters used across your whole website and will also be enough for several months. That's why I've decided to go for the latin set instead of crawling my page as this could lead to font changes with every new article I publish.