After images, fonts are usually the heaviest thing a page downloads — and unlike images they block what the reader sees, because text cannot be drawn until the browser knows what to draw it in. Four decisions account for nearly all of the cost. Three of them cost nothing to get right.
One: the format
A WOFF2 is around 60% smaller than the .ttf it was made from, and every browser still receiving security updates reads it. If you are serving anything else, this is a free saving and it takes one conversion. Old snippets listing EOT, SVG and TTF fallbacks are shipping files nothing requests — see the format guide.
Two: the character set
This is the big one and the one almost nobody does. A font carrying Cyrillic and Greek makes an English-language site download two alphabets it will never draw. Cutting to Latin commonly halves the file and sometimes takes three quarters off it. Every font page here offers both, with the measured sizes on the buttons, and the subsetting post explains what is being removed.
Three: how many files
Four static weights is four downloads. The same family as a variable font is one file that is usually smaller than three of those four — which is why a variable font is a performance decision and not only a design one, as long as you actually use more than two weights. If you use exactly one weight, a static file is smaller.
The other half of this is discipline. Two families is a reasonable ceiling for most sites. Three is a decision to justify. A page loading five is usually a page where nobody has looked at the network tab.
Four: when the download starts
A font referenced from a stylesheet is not requested until the browser has parsed that stylesheet, which is already late. A rel="preload" as="font" crossorigin link in the head starts it in parallel instead. Preload only what is on the first screen — preloading everything makes the page slower, because the requests compete with each other.
Then decide what the reader sees in the meantime. font-display: swap shows fallback text immediately and swaps, at the cost of a visible shift. font-display: optional gives the font a very short window and gives up for that page view if it misses, so nothing ever shifts.
The layout shift nobody attributes to fonts
When the real font swaps in, every line of text re-measures. If the fallback is much narrower or wider, the page jumps — and Cumulative Layout Shift is a ranking signal, so this is not only an aesthetic problem.
The fix is to make the fallback closer in metrics to the real font, using size-adjust, ascent-override and descent-override on an @font-face rule that points at a local system font. It is fiddly and it is the difference between a visible jump and none.
A realistic target
- WOFF2 only. Delete every other format from your CSS.
- Latin cut unless you genuinely set another alphabet.
- One variable file, or two static weights. Not six.
- Preload what is above the fold, and nothing else.
- Set font-display deliberately rather than leaving it to the default.
Done properly, a page with one variable family in a Latin cut is spending somewhere between 20 and 60 KB on typography. Done carelessly, the same design costs half a megabyte. The design is identical either way, which is what makes this worth twenty minutes.