← Index
Guide · Frontend

Self-hosting Google Fonts

Pull a typeface off the CDN and serve it yourself — for faster first paint, offline builds, and one less third-party request.

Updated Apr 2025 Time ~15 min Level Beginner Stack any static site

The <link> tag is the fastest way to get a Google Font on the page, but it costs you a round trip to a third-party origin and gives you no control over caching. Self-hosting fixes both. Here’s the whole procedure, start to finish.

Note

This assumes you can place static files in your project and edit one CSS file. No build step is required, though one helps for cache-busting.

Download the font files

Grab the source files from the typeface’s specimen page or its GitHub repo. You want the variable .ttf if one exists — a single file covers every weight.

Clone the upstream repo

Most Google Fonts are developed in the open. Pull the latest release rather than scraping the CDN.

bash
# grab the variable source for Fraunces
git clone https://github.com/undercasetype/Fraunces.git
cd Fraunces/fonts/variable

Keep only what you ship

You rarely need every axis and language subset. Note the files you'll convert next.

Convert to WOFF2

Browsers want .woff2 — it’s the smallest format and universally supported. Convert with Google’s woff2 tool or fonttools.

bash
# option A — the official compressor
woff2_compress Fraunces.ttf

# option B — fonttools, if you also want to subset
pip install fonttools brotli
pyftsubset Fraunces.ttf --flavor=woff2 \
  --unicodes=U+0000-00FF \
  --output-file=fraunces.woff2
Tip

Subsetting to the Latin range alone can shave 60–80% off a variable font's weight. If your blog is English-only, do it.

Declare the @font-face

Drop the files in /fonts and point CSS at them. For a variable font, declare the full weight range once.

css
@font-face {
  font-family: "Fraunces";
  src: url(/fonts/fraunces.woff2) format("woff2");
  font-weight: 300 600;   /* variable range */
  font-display: swap;        /* show fallback first */
  font-style: normal;
}
Warning

Without font-display: swap the text stays invisible until the font loads (FOIT). On a slow connection that's a blank page — always set it.

Preload the critical font

Tell the browser to fetch your above-the-fold typeface early, in parallel with the CSS. This one is a plain fenced block — it picks up the site’s syntax theme and gets a copy button automatically.

<link rel="preload" href="/fonts/fraunces.woff2"
      as="font" type="font/woff2" crossorigin>

Verify it loaded

Open DevTools → Network → Font. You should see your file served from your own origin and no request to fonts.gstatic.com.

Format support reference

For a modern blog you only need WOFF2. The rest are here for context if you support legacy browsers.

Format Extension Use it?
WOFF2 .woff2 Yes — primary
WOFF .woff Only for very old browsers
TrueType .ttf Source only, don’t ship
EOT / SVG .eot No — obsolete