Before tackling how, why might you want to self-host?

Caching

Google’s CDN serves CSS with a short (24-hour) cache header. This means daily visitors to your site will likely have to wait for a round-trip to the CDN before your text renders.

$ curl -i 'https://fonts.googleapis.com/css?family=Roboto'
…
HTTP/1.1 200 OK
Cache-Control: private, max-age=86400

See also, related typekit post: regarding-the-flash-of-unstyled-text-in-chrome

Smaller font files

You may be getting larger fonts than you actually need. If you’re only using the font for a fancy header, or some static text like a website name, the smallest character set – latin – is probably overkill.

The unicode range field here shows what’s included:

@font-face {
  unicode-range: U+0000-00ff, U+0131, U+0152-0153, U+02bb-02bc, U+02c6, U+02da,
    U+02dc, U+2000-206f, U+2074, U+20ac, U+2122, U+2212, U+2215;
}

Visualised with fontforge:

I find you can get away with a ~50% size reduction for English-language text by restricting to ranges 20-7E,2013-2014,2018-19,201C-201D,2022,2026. I know for sure I’m never going to need the copyright symbol…

Privacy, availability

The privacy aspect’s somewhat contentious, and boils down to whether you trust Google’s word on what they do (and will do in the future) with the collected data. This stackoverflow issue discusses the main points.

Regarding availability: if you have users in China, you’ll want an alternative solution, as Google web properties are often blocked.


So, onto how

Self-hosting

First off, snippets like the above @font-face definition might give the impression that self-hosting fonts is complicated; but it’s really not. Assuming you’re only interested in dealing with “modern” browsers, there’s only two file formats to worry about - woff, and woff2. Woff2 is just a compressed form of woff, so generating it’s not going to introduce complications of the sort that generating svg from ttf might.

In summary, you need two things:

  • An @font-face definition that points to your font in its various weights, styles and encodings.
  • A set of font files, hosted on your server.

The excellent google-webfonts-helper site will generate those files for you, assuming you’re happy with one of the default character sets.

If you want to optimize those further, you need to look into subsetting the font to remove unwanted characters.

One possibility for this is to use the python fonttools package:

pyftsubset font.ttf
  --output-file=font.woff2
  --flavor=woff2
  --unicodes=U+20-7E

There’s also a load of JS-based solutions, some of which integrate with webpack. I tried font-subset-loader which in turn uses fontmin, but ran into an issue with whitespace getting mangled.

Ordinarily I’m a big proponent of being able to build everything with a single command, but the maintenance overhead and additional compilation time for JS-based solutions didn’t seem worthwhile, given how infrequently fonts are going to change.