Self-host your Google fonts efficiently

This post is a second revision of my old article on optimizing fonts for performant websites. I will do it on an Google fonts example. Using unoptimized fonts AND loading them from 3rd party servers is in my opinion a very bad combination.

Many websites can take advantage of this process to improve their Web Core Vitals and Lighthouse scores. This is the most common advice I give when I do performance audits.

If you are interested in how to subset fonts using glyphhanger, read the old version [1]. In this article I will describe how to do it without command line or installing any dependencies.

In my first article I also assumed that it’s obvious why it is a good idea to self-host Google Fonts instead of using its CDN. That was my mistake, so lets start from the beginning.

Why not use Google default code?

In my humble opinion you should self-host everything you can. Images, scripts, fonts, even if those scripts are meant to be loaded from the service provider. Having said that, there is one advantage of copying and pasting code: ease of use. But im afraid thats the end of advantages. Lets talk about risks and disadvantages.

SSL handshakes

It takes time to negotiate secure connection. If your website is fast enough, this might be your biggest problem. The more unique domains, the bigger the issue.

Personally I think that 3 unique domains is a max: One for HTML, one for assets, one for analytics. Ideally it should all be on one.

The way Google fonts work is:

This chain of requests is longer than you including font-face declarations in your CSS file. Additionally, both of those requests are from different domains, so you introduce 2 SSL handshakes to your critical path. Not optimal.

Point of failure

If the service goes down, gets slow, or whats worse, gets shutdown by creators, you don’t know how your website will behave. I count that as a big risk, even, or especially [2], for free Google services. Watch Harry Roberts [3] talk about 3rd party to learn more.

Attack vector

Everything you load from outside servers, increase the number of different servers that someone can break your website. If you load everything from one server, usually it means that there is only one way of hacking your site. If you load from 10 servers, any one of those servers is a potential security risk.

Download font

Go to https://google-webfonts-helper.herokuapp.com

  1. Select font, charset, variants (font weights),
  2. Choose CSS for modern browsers (woff, woff2),
  3. Copy the CSS to the notepad
  4. Download zip file with fonts and extract it somewhere
  5. Remove woff2 files (dont worry, we will get them later)

Webfonts helper

If you wonder what variants mean, here is a cheatsheet:

100     → thin
300     → light
regular → normal (400)
500     → medium
700     → bold
900     → black

Your directory should include only woff files.

Original woff files

Optimize (subset) it

Go to https://everythingfonts.com/subsetter

  1. Pick your first file
  2. Select Uppercase, Lowercase, Numerals, Basic Punctuation, Currency Symbols
  3. Select the EULA checkbox (only if its true)
  4. Generate Subset
  5. Repeat for all of your font files
  6. Extract new woff files

Subsetter

Your newly downloaded woff files should be a lot smaller than the originals.

Subsetted woff files

Generate woff2 files

Go to https://cloudconvert.com/woff-to-woff2

  1. Upload all your new woff files
  2. Click convert
  3. Download all files

Converting to woff2

Now we have subsetted woff2 in addition to our subsetted woff files. Those should be even smaller than woff files.

Subsetted woff2 files

File size comparison

Woff files: 21 KB → 12 KB – 43% improvement
Woff2 files: 16 KB → 7 KB – 57% improvement

In short, its worth it, but its not the biggest saving in this whole operation.
Now lets use those fonts and load them from our own domain.

Example CSS

In my experiment with Roboto font, this is the CSS I landed on. You can change font file names, but remember to add font-display: swap somewhere in the font-face definition. It is important [4].

I decided to use woff and woff2 because not all browsers support woff2. 95% do [5], but better to have a fallback.

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/fonts/roboto-v29-latin-300-subset.woff2') format('woff2'),
       url('/fonts/roboto-v29-latin-300-subset.woff') format('woff');
}

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/roboto-v29-latin-regular-subset.woff2') format('woff2'),
       url('/fonts/roboto-v29-latin-regular-subset.woff') format('woff');
}

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/roboto-v29-latin-500-subset.woff2') format('woff2'),
       url('/fonts/roboto-v29-latin-500-sub set.woff') format('woff');
}

Now that we have font files, and CSS files, we can use the font using combination of font-family: ‘Roboto’ and font-weight properties.

Visit demo if you are wondering how it works in the wild.

Footnotes

  1. https://dev.to/platformos/optimizing-font-files-for-modern-web-3jm8 ↩︎

  2. https://killedbygoogle.com/ ↩︎

  3. https://www.youtube.com/watch?v=bmIUYBNKja4 ↩︎

  4. https://css-tricks.com/almanac/properties/f/font-display/ ↩︎

  5. https://caniuse.com/woff2 ↩︎