Fonts occupy a unique position in performance: they block nothing in the browser’s first paint, yet their absence defines how broken the page feels. The strategies below are ordered by what we actually deploy on client themes, with the trade-offs measured.
First: the two failure modes
- FOUT (flash of unstyled text): text renders in fallback font, then swaps. With
font-display: swapthis is the default behavior — visible, slightly jarring, honest. - FOIT (flash of invisible text): text hidden until the font loads.
font-display: blockdoes this briefly — worst for LCP because the hero headline is often the largest element, and hidden text cannot paint.
Default stance: font-display: swap everywhere, with a well-chosen fallback stack so the swap is subtle.
Strategy 1: Fewer fonts, fewer weights (the free win)
Two families at four weights each means eight font files. Every audit we run finds at least two weights that load and never render (a 300 that no CSS rule uses, for instance). Cut to what you use — typically 2–4 files total. This is the highest-leverage font decision and costs nothing, and it compounds with the asset diet in the bloat audit.
Strategy 2: Preload the primary weight
The font your headline uses (usually the bold weight of the display family) deserves a preload in the document head — it starts downloading immediately instead of after CSS parsing:
<link rel="preload" href="/fonts/display-bold.woff2" as="font" type="font/woff2" crossorigin>
Measured on our test theme: ~120ms off the critical path. Preload one or two files maximum — preloading five fonts delays everything else.
Strategy 3: Self-host
Google Fonts loaded from Google’s CDN costs a DNS lookup plus the GDPR transmission of visitor IPs. Self-hosting removes the round trips entirely; the plugin routes are compared in the self-hosting comparison. On our test: LCP 1.42s → 1.21s.
Strategy 4: Subset and compress
woff2 subsets covering Latin only (drop the Cyrillic/Greek glyphs you do not use) shrink files 20–40%. Google’s unicode-range serving handles this automatically; self-hosted fonts should replicate it with subset files.
Strategy 5: Size your fallback
CLS from font swapping happens when fallback and webfont metrics differ wildly. Pick fallbacks with similar proportions (system stacks like system-ui, -apple-system, sans-serif) — the swap then shifts pixels barely at all, protecting the CLS threshold.
What we ship by default
Two families max, 3–4 weights, self-hosted woff2 subsets, font-display: swap, one preload, measured fallback. That stack survives audits, upgrades, and GDPR reviews alike. If your current numbers are unknown, the free audit reports font weight, request count, and their LCP contribution.
font-display: swap — text stays visible in a fallback while the webfont loads. Block hides text (hurting LCP); optional causes infinite invisible text on failed loads. No — preload one or two files maximum (the headline weight). Preloading many fonts delays every other critical asset and can worsen LCP. Yes — it removes DNS and connection round trips to Google's servers. Measured on our test theme: LCP improved from 1.42s to 1.21s, plus a GDPR benefit. Two families, 3–4 weights total. Most audits find unused weights loading and never rendering — cut those first; it is free performance.What is the best font-display setting for WordPress?
Should I preload all my fonts?
Is self-hosting Google Fonts faster?
How many fonts should a WordPress site load?