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:
- browser downloads CSS file, that has font-face declarations,
- after its downloaded and parsed browser starts to download font files
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
- Select font, charset, variants (font weights),
- Choose CSS for modern browsers (woff, woff2),
- Copy the CSS to the notepad
- Download zip file with fonts and extract it somewhere
- Remove woff2 files (dont worry, we will get them later)
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.
Optimize (subset) it
Go to https://everythingfonts.com/subsetter
- Pick your first file
- Select Uppercase, Lowercase, Numerals, Basic Punctuation, Currency Symbols
- Select the EULA checkbox (only if its true)
- Generate Subset
- Repeat for all of your font files
- Extract new woff files
Your newly downloaded woff files should be a lot smaller than the originals.
Generate woff2 files
Go to https://cloudconvert.com/woff-to-woff2
- Upload all your new woff files
- Click convert
- Download all files
Now we have subsetted woff2 in addition to our subsetted woff files. Those should be even smaller than woff 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.