How to Optimize Web Fonts for Performance
Web fonts can block rendering, hide text and cause layout shift. How to optimise them with woff2, subsetting, font-display, preloading and self-hosting.
Typography makes a site feel considered — but custom web fonts are not free. Every font is a file the browser has to download, and until it arrives the browser must decide between hiding your text or showing it in a fallback that will later jump. Mishandled, fonts delay your first paint, disrupt reading, and shove your layout around. In short: web fonts cost rendering time and visual stability, and you tame them with woff2, subsetting, font-display, preloading key files, fewer weights, and a well-chosen fallback — protecting FCP, LCP and CLS together. This guide walks through the cost of fonts and every fix, with a checklist and a decisions table you can apply today.
It is a deep-dive on a lever that appears throughout how to make your website load faster.
Why web fonts cost performance
A custom font is an extra resource on the critical path for text, and it creates three distinct problems.
First, render delay and FOIT. Text styled with a custom font cannot display in that font until the file downloads. With some loading strategies the browser shows a flash of invisible text (FOIT) — blank space where words should be — until the font is ready, which pushes back First Contentful Paint on any text-first page and leaves users staring at nothing.
Second, reflow and FOUT. The alternative is to show a fallback font immediately and swap to the custom one when it loads — a flash of unstyled text (FOUT). Reading can start sooner, but when the swap happens the text often reflows because the two fonts have different letter widths and line heights.
Third, layout shift (CLS). That reflow is not just cosmetic: if the custom font is taller, wider or differently spaced than the fallback, surrounding content moves, registering as Cumulative Layout Shift. And if the font is also part of or near your largest visible element, slow font loading can even drag on Largest Contentful Paint, because a text block can be the LCP element.
So fonts uniquely touch three things at once — first paint, the largest paint, and visual stability — which is why getting them right pays off across several metrics simultaneously.
Serve woff2 and nothing heavier
Format is the first lever. woff2 is the most compressed font format with broad modern-browser support, and it is meaningfully smaller than older formats such as TTF, OTF or the original WOFF. For the overwhelming majority of audiences you can serve woff2 alone and not bother with fallback formats at all; only if you must support genuinely ancient browsers would you add an older format, which is rarely necessary now. Smaller files arrive sooner, which shortens both the FOIT/FOUT window and any associated shift. If your fonts are currently served as TTF or WOFF, converting to woff2 is a quick, pure win.
Subset to the characters you actually use
A full font file contains glyphs for many languages, symbols and styles you almost certainly do not need. Subsetting strips a font down to only the characters you use — for an English-language site, for example, the basic Latin set plus a handful of punctuation marks — which can cut file size dramatically. You can subset by character range (keeping, say, Latin and dropping Cyrillic, Greek and CJK) or, for fixed text like a logo or heading, by the exact glyphs required. Many font hosts offer subsetting via URL parameters, and tooling exists to subset self-hosted fonts at build time. The smaller the file, the faster text settles into its final form.
Control the loading behaviour with font-display
The CSS font-display descriptor decides what the browser does while a font loads, and it is the single most important font-performance setting. The main values:
| Value | Behaviour | Best for |
|---|---|---|
swap | Show fallback immediately, swap to custom font when ready | Most body and heading text — text is readable at once |
optional | Show fallback; use the custom font only if it loads almost instantly (else keep fallback this visit) | Performance-critical pages; avoids any late swap and its shift |
fallback | Brief invisible period, then fallback, then swap within a short window | A compromise between swap and optional |
block | Hide text for a short period waiting for the font (risks FOIT) | Rarely advisable for content text |
auto | Browser default (often behaves like block) | Avoid relying on it; set a value explicitly |
For most sites, swap is the sensible default: it eliminates FOIT so users can read immediately, accepting a brief reflow you then minimise with a good fallback (below). Where you would rather avoid any late swap and the shift it can cause, optional is excellent — the browser uses the custom font only if it is essentially already available, otherwise it sticks with the fallback for that visit and the page never jumps. Choosing between them is a judgement about how much you value the exact typeface versus zero layout shift.
Preload the one or two fonts that matter
The browser only discovers a font when it parses the CSS that references it, which can be late. A <link rel="preload" as="font" type="font/woff2" crossorigin> hint tells the browser to fetch a critical font early, so it arrives sooner and the swap happens faster. Reserve this for the one or two fonts used above the fold — typically your body text font and a primary heading font. The common mistake is preloading everything: preloading many fonts congests the network and delays other critical resources like the LCP image, which is self-defeating. Preload surgically — the handful of fonts that block visible text — and let the rest load normally.
Limit families and weights
Every font family, and every weight and style within it, is a separate file to download. A page that loads regular, italic, medium, semibold and bold across two families is fetching ten files before its text is fully styled. Be disciplined: limit the number of families (one or two is plenty for most designs) and only load the weights you actually use. If you use a weight in one rarely-seen component, consider whether the design truly needs it. Variable fonts can help here — a single variable font file can provide a continuous range of weights, sometimes replacing several static files with one, though you should weigh its size against how many weights you genuinely use.
Self-host, or preconnect to the font host
Where you load fonts from affects how fast they arrive. Self-hosting — serving font files from your own domain or CDN — removes a third-party dependency: the browser does not have to perform a separate DNS lookup, connection and TLS handshake to another origin before it can even request the font, and you gain full control over caching, format and subsetting. This is often the faster choice and is increasingly recommended.
If you do use a third-party font host, add a <link rel="preconnect"> to that origin (and to any separate domain it serves the font files from) so the connection handshake is done in advance, ready for when the font is requested. Without preconnect, the browser pays the full connection cost at the moment it discovers the font, adding avoidable delay. Either way, the goal is the same: shorten the time between "page starts loading" and "the font file begins downloading".
Choose a fallback that minimises shift
Even with swap, the swap from fallback to custom font moves text if the two fonts have different metrics — and that movement is layout shift. You reduce it by choosing a fallback font whose dimensions are close to your custom font, and by using the CSS font descriptors size-adjust, ascent-override, descent-override and line-gap-override to tune the fallback so it occupies almost exactly the same space. Done well, the swap becomes nearly invisible because the text barely moves. This is the detail that separates a font setup that quietly passes Cumulative Layout Shift from one that fails it on slow connections.
Consider system fonts where appropriate
The fastest font is the one you never download. A system font stack — using the fonts already installed on the user's device (such as the system-ui family) — has zero download cost, renders instantly, and causes no FOIT, FOUT or shift. It will not match a bespoke brand typeface, so it is not right for every project, but for performance-critical interfaces, body text, or sites where typography is not a core part of the brand, system fonts are a legitimate and increasingly popular choice. Even a hybrid approach — a custom font for headings, system fonts for body text — cuts the font payload substantially while keeping a distinctive look where it counts most.
How fonts tie into Core Web Vitals
It is worth being explicit about why this work matters for the metrics that count.
- First Contentful Paint: FOIT hides text until the font loads, delaying the first paint on text-first pages.
font-display: swapand preloading fix this. - Largest Contentful Paint: when the LCP element is a text block, a slow font delays it; when it is an image, a fonts-heavy critical path can still compete for bandwidth. Subsetting, woff2 and limited weights help.
- Cumulative Layout Shift: the fallback-to-custom swap reflows text and moves content. A metrically matched fallback (and
optional) keeps shift negligible.
Because one set of fixes improves all three, fonts are an unusually high-leverage area — a few hours of work can lift several metrics at once. For the metrics themselves, see Core Web Vitals explained.
A worked example
Imagine a content site loading two families at five weights total, served as TTF from a third-party host, with no font-display set. PageSpeed Insights flags render-blocking text and a field CLS of 0.18. You make a focused pass: convert the fonts to woff2 and subset them to Latin, which cuts their combined size by well over half; drop the design to one family at two weights after confirming the others were barely used; self-host the files and preload the body font; set font-display: swap with a metrically-matched fallback tuned via size-adjust. The result: text now paints immediately in a near-identical fallback, the custom font swaps in with barely perceptible movement, FCP improves because nothing blocks the first paint, and field CLS drops to around 0.03. This is the typical shape of font work — several small, compounding changes that together move multiple metrics.
A font-optimisation checklist
- Serve fonts as woff2 (drop heavier formats unless you truly need them).
- Subset to the characters and languages you actually use.
- Set
font-display: swap(oroptional) on every@font-face. - Preload only the one or two above-the-fold fonts.
- Limit families and weights; consider a variable font.
- Self-host, or preconnect to the font host.
- Choose a metrically-matched fallback and tune it with
size-adjustand overrides. - Consider system fonts for body text or non-brand interfaces.
- Re-measure FCP, LCP and CLS in the field after changing fonts.
Common mistakes
- Leaving
font-displayunset, defaulting to behaviour that can hide text (FOIT). - Serving full, unsubsetted fonts in heavy formats like TTF.
- Preloading every font, congesting the network and delaying the LCP resource.
- Loading many weights "just in case", each a separate download.
- Ignoring the fallback, so the swap reflows text and inflates CLS on slow connections.
Go deeper
- The big picture, in priority order: how to make your website load faster.
- The first paint fonts can delay: what is First Contentful Paint (FCP).
- The largest paint fonts can affect: what is Largest Contentful Paint and how to improve it.
- The full set of metrics: Core Web Vitals explained.
Want to see how your fonts affect performance, alongside SEO, security and AI-readiness? Analyse any URL with StackOptic — free, no sign-up.
Frequently asked questions
Why do web fonts hurt performance?
Web fonts are additional files the browser must download, and they sit on the critical path for showing text. Until a custom font loads, the browser either hides the text (a flash of invisible text, FOIT) or shows a fallback that reflows when the real font arrives (a flash of unstyled text, FOUT). Both delay or disrupt the experience, and the reflow can cause layout shift. Each extra family and weight is another file to fetch.
What is the best font format for the web?
Woff2 is the best choice for the web. It is the most compressed font format with broad support across modern browsers, typically much smaller than older formats like TTF or the original woff. Serving woff2 alone is fine for the vast majority of audiences; you only need older formats as fallbacks if you must support very old browsers, which is rarely necessary today. Smaller font files mean text renders sooner.
What does font-display: swap do?
font-display: swap tells the browser to show text immediately using a fallback font while the custom font downloads, then swap to the custom font once it arrives. This eliminates the flash of invisible text (FOIT), so users can start reading right away and First Contentful Paint is not held up by the font. The trade-off is a brief reflow when the swap happens, which you minimise by choosing a metrically similar fallback.
Should I preload my fonts?
Preload the one or two most important fonts — typically the body text font and a primary heading font used above the fold. A preload link tells the browser to fetch the font early, before it would otherwise discover it in the CSS, so the font arrives sooner and the swap happens faster. Do not preload every font, though; preloading too many congests the network and delays other critical resources, defeating the purpose.
Is it better to self-host fonts or use Google Fonts?
Self-hosting is often better for performance because it removes a third-party connection: the browser does not have to do a separate DNS lookup, connection and TLS handshake to another domain before it can fetch the font. Self-hosting also gives you full control over caching, format and subsetting. If you do use a third-party font host, add a preconnect hint to that origin so the connection is ready when the font is requested.
Analyse any website with StackOptic
Get the full technology stack, performance, security and SEO report in seconds — free.
Analyse a websiteRelated articles
How to Test Your Website Speed (Tools and Metrics)
Which tools to use, lab versus field data, and the metrics that matter. Test your site speed with PageSpeed Insights, Lighthouse, WebPageTest and DevTools.
How to Optimize CSS for Faster Pages
CSS is render-blocking, so bloated stylesheets delay your page. How to minify, cut unused CSS, inline critical CSS and defer the rest to speed up FCP and LCP.
What Are Gzip and Brotli Compression (and How to Enable Them)?
Gzip and Brotli shrink HTML, CSS and JavaScript before they are sent. What each is, how Brotli compares to Gzip, how to verify it, and how to enable it.