Self-hosting Google fonts
Google Fonts is an incredibly popular way to use custom fonts on your web pages. You can choose from hundreds of open source fonts in Google’s library, and it’s all fast and free.
The thing is, handy as Google Fonts might be, it’s still a third-party dependency. Every new visitor to your site means another trip to Google’s servers, fetching that fancy font you want them to see. Google says their Fonts API is “designed to limit the collection, storage, and use of end-user data to what is needed to serve fonts efficiently”,1 but nonetheless, they’re tracking your visitors, so you at the very least need to include Google Fonts in your site’s Privacy Policy.
Sometimes it feels good to just cut out the third-party tracking, and serve up the fonts yourself. And thanks to widespread support for WOFF and WOFF2 font formats, it’s fairly easy to do.
Here’s how I did it in a recent project.
But first, a word about licensing
It’s simple: Only convert a TTF to a webfont if you have permission to do so.
Fonts released under an open source license (such as the SIL OFL, or Apache 2.0) are all free to use, modify, and republish. So they’re good to go!
Most commercial font licenses, on the other hand, explicitly forbid publication as webfonts. It’s a pain, but hey, those designers gotta earn a living.
Here are a few places to find really awesome open source fonts:
- Google Fonts
- Open Foundry
- Open Font Library
- The League of Moveable Type
- Font Space – ‘open’ category
Getting TTF files
You’ll want to start with TTF (TrueType) files for a font. Google Fonts has recently added a “Download family” button to the font family pages on its site, like this one, for Open Sans. Clicking the button will download a zip archive containing a TTF file for each weight and style of the font.
For each TTF file you download, we’ll create a corresponding WOFF and WOFF2 file. This will give you fairly good browser support.
Then, finally, we’ll reference those WOFF and WOFF2 font files in your site’s CSS.
Let’s begin!
Building WOFF files
WOFF is a compressed font format, supported by most web browsers released after 2011, including IE9–IE11.2
You can convert a TTF file to WOFF file using the ttf2woff nodejs script. Here I am downloading the script, and running it on a OpenSans-Regular.ttf
file I already downloaded:
git clone --recursive https://github.com/fontello/ttf2woff.git
cd ttf2woff
npm install
./ttf2woff.js OpenSans-Regular.{ttf,woff}
ttf2woff.js
requires two arguments – a source .ttf
file to convert, and a destination .woff
file to create.
To save writing out the font’s filename twice, I’ve used the Bash shortcut {ttf,woff}
. When the command runs, Bash spots the curly brackets, and expands the two arguments out (ie: myfont.{ttf,woff}
becomes myfont.ttf myfont.woff
).
Building WOFF2 files
WOFF2 is a more efficient compression format, supported by almost all modern browsers, including Edge (14+), Firefox (39+), Chrome (36+), and Safari (12+).3
Google maintains a command-line script to create WOFF2 files, in its woff2 library:
git clone --recursive https://github.com/google/woff2.git
cd woff2
make clean all
./woff2_compress OpenSans-Regular.ttf
woff2_compress
automatically creates a .woff2
file with the same name as the input .ttf
file.
Writing your CSS
Now you can pop the .woff
and .woff2
files somewhere in your web directory, and reference them using @font-face
rulesets in your CSS.
Here’s an example of me serving Open Sans in two styles (regular and italic) and two weights (regular and bold):
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/open-sans-regular.woff') format('woff'), url('/fonts/open-sans-regular.woff2') format('woff2');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-display: swap;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/open-sans-italic.woff') format('woff'), url('/fonts/open-sans-italic.woff2') format('woff2');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/open-sans-bold.woff') format('woff'), url('/fonts/open-sans-bold.woff2') format('woff2');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 700;
font-display: swap;
src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/open-sans-bold-italic.woff') format('woff'), url('/fonts/open-sans-bold-italic.woff2') format('woff2');
}
It’s good practice to include the local()
functions so that, if the user already has the font installed on their device, that local copy is used, instead of your webfont versions, saving a few precious bytes. But if you don‘t know the font’s name and Postscript name, you can leave this bit out.
Subsetting fonts
One nice thing that Google Fonts makes easy is requesting just a subset of the glyphs contained in a font. So if you know you’ll only ever need latin characters, say, you can ask Google Fonts to serve you a version of a font without any cyrillic or Vietnamese glyphs, often dramatically reducing the filesize.
If you want to try this yourself, you’ll need to install a few additional tools:
npm install -g font-ranger
pipx install fonttools
pipx inject fonttools brotli zopfli
And then run font-ranger
over your TTF files, to generate the individual subsets:
for i in Montserrat-*.ttf; do font-ranger -f "$i" -o ../webfont-subsets -u latin latin-ext cyrillic cyrillic-ext vietnamese -n "${i%.*}" -w true; done
Good luck!