2026-08-21
Serving fonts from your own domain removes a DNS lookup and a TLS connection to a third party, and browsers no longer share cached fonts between sites anyway. Doing it well takes four steps.
Every browser in use supports WOFF2, and it is about 30% smaller than WOFF. TTF and EOT fallbacks only add bytes to the stylesheet.
A full Latin font with every glyph can be 100 KB per weight. If the site is in English, keep Basic Latin, Latin-1 punctuation and the few symbols you use:
pyftsubset Inter.ttf --flavor=woff2 --output-file=inter-latin.woff2 \ --unicodes="U+0000-00FF,U+2013-2014,U+2018-201D,U+2026,U+20AC" --layout-features='kern,liga'
Declare the same range in unicode-range, so a page that happens to contain other
characters can still fall back to a second file instead of rendering boxes.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
unicode-range: U+0000-00FF, U+2013-2014, U+2018-201D, U+2026, U+20AC;
}
swap shows the fallback font immediately and replaces it when the web font
arrives. For body text this is the right choice; the cost is a visible reflow, which
size-adjust and ascent-override on the fallback face can make almost
invisible.
<link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
The crossorigin attribute is required even on the same origin, because fonts are
fetched in CORS mode; without it the preloaded file is downloaded twice. Preload only what is used
above the fold. Every preload competes with the HTML and CSS for the first round trips.
A variable font, as in the example above, replaces several weight files with one, which usually makes the total smaller once more than two weights are in use.