Font Chargement

How web font chargement affecte Core Web Vitals comme CLS and LCP, font-display strategies, preloading fonts, and reducing layout shift from font swaps.

Première publication : 2 juil. 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues

Custom web fonts are external fichiers que have to download avant text peut render in les, so le navigateur nécessite a plan pour ce que to montrer in the meantime. Que plan — définir mainly by the CSS font-display property — moves two Core Web Vitals: LCP (quand a render-blocking font delays votre largest text) and CLS (quand swapping the fallback pour the web font shifts the layout). The two par défaut behaviors, FOIT and FOUT, chaque have a échec mode. The reliable fixes: preload critical fonts, utiliser font-display: optional or swap deliberately, match votre fallback font's metrics, or skip custom fonts entirely. It's pas a direct ranking factor — it feeds CLS/LCP, qui are page experience signals.

TL;DR — Custom web fonts are render-affecting external resources. Ils hit LCP quand a render-blocking font delays votre largest text element, and CLS quand the fallback-to-web-font swap reflows the layout. The two par défaut behaviors — FOIT (invisible jusqu’à chargé) and FOUT (fallback alors swap) — chaque échouer differently. font-display is the principal lever (swap guarantees FOUT; optional donne a ~100ms window and alors commits, killing the plus tard swap). Preloading fixes late font discovery; preload + font-display: optional is the combo Google’s propre engineering singled out pour eliminating layout jank. But passing the Lighthouse font-display vérifier doesn’t fix CLS — que nécessite a metrically-matched fallback (or size-adjust/ascent-override overrides). None of ce is a direct ranking factor; it feeds CLS/LCP, qui are page experience signals.

Evidence for this claim The CSS Font Loading API exposes font loading state and control to documents. Scope: Current official or standards documentation. Confidence: high · Verified: MDN: CSS Font Loading API Evidence for this claim font-display controls how a font face is displayed while it downloads and when fallback is used. Scope: Current official or standards documentation. Confidence: high · Verified: MDN: font-display

Pourquoi fonts are a special cas

La plupart performances problems are à propos de bytes — an image is aussi big, a script blocks the principal thread. Fonts are worse que que in un spécifique façon: they’re an external resource and ils modifier the metrics of votre text. So a font peut hurt vous two différent façons at une fois. It peut delay quand text paints (an LCP problem), and it peut modifier the size and shape of que text après it’s déjà on screen (a CLS problem).

The two par défaut navigateur behaviors are worth naming parce que the whole topic hangs off les:

  • FOIT (Flash of Invisible Text): text in the custom font is rendered invisible jusqu’à the font loads or a timeout is hit — historically up to a ~3-second block in la plupart navigateurs.
  • FOUT (Flash of Unstyled Text): the fallback font montre immédiatement, alors obtient swapped pour the web font une fois it loads.

FOIT feels “cleaner” (aucun jump) but risks a rendering delay. FOUT feels faster (text now) but risks a layout shift on swap. Neither is automatically safe — that’s the core idea to hold onto.

It aussi helps to separate the pipeline into distinct stages, parce que success at un stage doesn’t guarantee success at the suivant. A @font-face rule seulement declares a face — le navigateur récupère the réel font fichier seulement une fois matching styled text on lune page exige it (declaration and matching are separate steps from the récupérer). Que récupérer is subject to CORS si the font is served cross-origin, so a stylesheet que loads cleanly doesn’t guarantee the font réponse itself carries the correct cross-origin permission — a manquant header là fails silently as a font que jamais renders. And a successful récupérer encore doesn’t guarantee a stable-looking page: cold-cache, warm-cache, and already-cached font states peut produce différent discovery and swap timing, so testing un repeat page charger in DevTools doesn’t tell vous ce que a first-time visitor en réalité sees.

How fonts affecter LCP

Si the largest visible element on votre page is text — a big headline, a hero paragraph — alors the font que text uses peut become partie of votre LCP chemin. Per web.dev’s LCP guidance, the LCP resource of une page, si it has un, va be soit an image or a web font. A render-blocking font que leaves que text invisible (FOIT) pushes votre LCP out jusqu’à the font arrives.

The fix ici is straightforward and comes straight from Google. On web.dev’s Optimize Plus grand affichage de contenu guide:

“Si vous définir a font-display valeur of anything autre que auto or block, alors text va toujours be visible during charger, and LCP won’t be blocked on an additional network requête.”web.dev

En d’autres termes: swap, fallback, or optional tout garder text visible during charger, so votre LCP isn’t hostage to the font requête.

How fonts affecter CLS

CLS comes from the swap. Votre fallback font and votre web font almost jamais have identical character widths and line heights, so quand le navigateur replaces un with the autre, the text reflows — and everything ci-dessous it moves. That’s a layout shift.

The counterintuitive partie, and the chose la plupart personnes miss: FOIT causes CLS aussi. Même invisible text is encore laid out en utilisant the fallback font’s metrics, so the swap encore moves choses. From web.dev’s Optimize Décalage cumulatif de mise en page guide:

“Les deux approaches peut causer layout shifts. Même si the text is invisible, it’s encore laid out en utilisant the fallback font, so quand the web font loads, the text block and the surrounding content shift”web.dev

The même page frames the two behaviors plainly:

“The fallback font is swapped with the web font, incurring a Flash of Unstyled Text (FOUT). ‘Invisible’ text is affiché en utilisant the fallback font jusqu’à a web font is disponible and the text is made visible (FOIT—flash of invisible text).”web.dev

I’ve said the même chose in plainer terms in my Ahrefs write-up on Décalage cumulatif de mise en page: quand a font loads or changements, vous fin up with a noticeable shift — a FOIT or a FOUT. It’s un of the la plupart courant real-world CLS causes, correct alongside images sans dimensions and content injected après charger.

The font-display property

font-display is a @font-face descriptor que contrôle two windows: the block period (how long le navigateur hides text waiting pour the font) and the swap period (how long, après the block period, it va encore swap in the web font une fois it arrives). Per web.dev’s Meilleur Practices pour Fonts:

ValeurBlock periodSwap period
autoBrowser-dependentBrowser-dependent
block2–3 secondsInfinite
swap0msInfinite
fallback100ms3 seconds
optional100msNone

MDN’s font-display référence, qui Google’s propre docs lien to as the spec source, describes les tersely: swap donne an extremely petit block period and an infinite swap period; optional donne an extremely petit block period and aucun swap period. Que “no swap period” is the whole point of optional — après the block period window closes, whatever font is in utiliser stays, so there’s aucun plus tard swap to shift the layout.

Un caution on the numbers in que table: the CSS specification itself seulement defines block/swap period categories — short, extremely petit, infinite, none — and leaves the exact duration to le navigateur. The millisecond/second figures ci-dessus are web.dev’s documented Chromium behavior, pas a cross-browser guarantee. Treat les as illustrative plutôt que a spec promise, and vérifier current numbers pour whichever navigateur/version you’re en réalité testing contre.

Qui valeur pour qui content? web.dev’s propre advice is que ces peut be mixed: utiliser swap pour branding and autre visually distinctive elements (où seeing the brand font matters and a brief shift is acceptable), and optional pour corps text (où CLS-sensitivity wins and you’d plutôt pas shift). That’s a genuinely utile rule of thumb — don’t appliquer un valeur site-wide by reflex.

Preloading fonts

Font requêtes are découvert late. Le navigateur doesn’t know it nécessite a font jusqu’à it has parsed votre CSS, matched a @font-face rule to an element on lune page, and decided que element is visible. Seulement alors fait it commencer the download. A <link rel="preload"> short-circuits que:

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

Que indique le navigateur to commencer fetching the font immédiatement, in parallel with everything sinon, au lieu de waiting to découvrir it. Bing’s engineering team describes exactly ce mechanism on leur propre search pages (plus in the Quotes tab): the preload tags in the <head> kick off the font downloads correct away, whereas sans les le navigateur wouldn’t récupérer the fonts jusqu’à it had parsed the CSS and trouvé matching elements.

The strongest combination per Google’s propre writeup is preload + font-display: optional. web.dev’s Prevent layout shifting and FOIT by preloading optional fonts calls combining <link rel="preload"> with font-display: optional the la plupart effective façon to guarantee aucun layout jank quand rendering custom fonts, and notes que Chrome (from version 83) eliminated the layout shifting que utilisé to se produire quand preloading optional fonts. The preload donne the font its meilleur shot at arriving à l’intérieur the ~100ms window; optional guarantees que si it doesn’t, vous don’t pay pour it with a shift.

Quand pas to preload. Preloading isn’t free. web.dev warns que preload is highly effective at making fonts discoverable early, but it comes at the cost of taking navigateur resources away from chargement autre resources. Preload the un or two fonts vous en réalité besoin ci-dessus the fold — preloading every font weight vous propre peut starve plus important resources and faire lune page slower overall.

Assurez-vous the preload en réalité obtient reused. A preload seulement helps si le navigateur peut match it to the réel font requête — the href, as="font", type, and crossorigin mode tout have to line up with ce que the CSS @font-face rule ends up requesting, or you’ll obtenir an “unused preload” warning and a wasted second download au lieu de a faster un. Preloading peut aussi bypass unicode-range selection, pulling bas a subset vous didn’t en réalité besoin pour que page. Confirmer the match in DevTools’ Network panel: vous vouloir to voir un requête pour que font, kicked off by the preload, pas two.

Fixing the shift itself: fallback metric matching

Here’s the trap: passing the Lighthouse font-display vérifier fait pas fix CLS. Google’s Lighthouse docs are explicit que FOIT and FOUT have the même impact on CLS une fois the custom font replaces the temporary system font. So swap rend votre text visible (bon pour LCP and pour the “invisible text” audit) but leaves the swap shift in placer.

To kill the shift quand a swap fait se produire, vous have to faire the fallback font and the web font prendre up the même space. Two levers:

  1. Pick a metrically-similar system fallback in votre font-family stack, plutôt que a unique custom font nom with nothing sensible behind it. The closer the fallback’s character widths and line height are to the web font, the plus petit the shift.
  2. Override the web font’s metrics with size-adjust, ascent-override, descent-override, and line-gap-override on the @font-face rule, so the web font is forced to match the fallback’s box. web.dev listes ces descriptors, and Smashing Magazine’s deep dive on CSS font descriptors walks via les in detail si vous vouloir to go tout the façon.

Chaque of ceux four descriptors adjusts a différent metric, and current navigateur prise en charge varies by descriptor — vérifier compatibility avant leaning on quelconque unique un. Validate contre the réel weights, styles, and scripts vous ship, aussi: a fix confirmed on votre regular-weight Latin text doesn’t guarantee bold, italic, or a non-Latin script behave the même façon.

DebugBear’s Fixing Layout Shifts Caused by Web Fonts is a bon end-to-end walkthrough of ce: mesurer the impact, identifier the problem font, appliquer font-display, choisir a meilleur fallback, alors adjust font metrics.

Google Fonts vs. self-hosting

Vous don’t have to self-host to contrôler font-display. The Google Fonts CSS2 API accepts a display requête parameter — https://fonts.googleapis.com/css2?family=Roboto&display=swap sets font-display directement on the served @font-face rules. Addy Osmani’s post on shipping font-display to Google Fonts covers the modifier: previously the seulement façon to specify font-display pour Google Fonts was to self-host les, and ce supprimé que besoin.

Self-hosting encore wins on un chose: it eliminates the supplémentaire DNS lookup and connection to fonts.googleapis.com and fonts.gstatic.com. That’s a réel latency cost, and un of the raisons a preconnect resource hint to ceux origins helps si vous do utiliser Google Fonts. Weigh the connection overhead contre the convenience — and don’t assume “it’s from Google, so it’s fine.” A copy-pasted Google Fonts embed is encore a render-blocking stylesheet requête plus a font requête, and older embeds may pas carry a display valeur at tout.

There’s aucun universal winner entre the two options — it comes bas to qui scripts and weights vous en réalité ship, votre mise en cache setup, and the font’s license terms. Run the comparison contre votre propre pages plutôt que defaulting to whichever un a tutorial recommended.

Variable fonts

A variable font packs nombreux weights and styles (regular, bold, condensed, italic) into a unique fichier with adjustable axes. Que peut be a net win — un requête à la place of six — si vous en réalité utiliser several of ceux variants. But a complet variable font is a bigger unique fichier que un static weight, so si vous seulement ever render un weight, shipping the whole variable font peut be slower, pas faster. Subset it to the axes and characters vous utiliser, serve it as WOFF2 so it’s compressed, and vérifier the font’s license — pas every foundry permits subsetting or self-hosting.

Reducing the fichier in the premier placer

Everything ci-dessus manages quand the font loads. Vous pouvez aussi shrink ce que loads. Font subsetting strips out glyphs vous don’t utiliser (an all-Latin site doesn’t besoin Cyrillic and CJK ranges), and WOFF2 is the well-compressed format to serve. A plus petit font fichier is plus probable to arrive à l’intérieur optional’s window and moins probable to block LCP — subsetting and compression are the complementary lever to font-display and preload, pas an alternative to les.

There’s aucun unique “correct” subset or universal byte-savings percentage ici, though — it dépend on the scripts, characters, and weights votre spécifique audience en réalité nécessite. A number vous saw in someone else’s cas study or benchmark isn’t a promise pour votre pages; mesurer votre propre font requêtes avant and après.

Is ce a ranking factor?

Be precise ici. Font chargement is pas a direct ranking factor. Ce que it affecte is CLS and LCP, qui are Core Web Vitals and partie of Google’s page experience signals. And page experience is a lightweight, tiebreaker-style signal — relevance and quality dominate; it helps decide entre sinon comparable results, pas entre a great page and a poor un. So the honest framing is: fix votre font chargement parce que it’s a genuine user-experience problem que se produit to feed two ranking inputs — pas parce que a font-display valeur va déplacer vous up le résultats on its propre.

My propre prendre

À travers my Ahrefs CLS and LCP guides I’ve landed on the même priority order, and it encore holds. Si vous pouvez utiliser a system font, do que — there’s nothing to charger, so là are aucun delays or changements que causer a shift. Si vous have to utiliser a custom font, the current meilleur méthode pour minimizing CLS is to combine <link rel="preload"> (grab the font as soon as possible) with font-display: optional (give it a petit window to charger); si it doesn’t faire it in temps, lune page simplement montre a par défaut font, and votre custom font obtient mis en cache and montre up on subsequent loads. Preload, alors optional, alors — meilleur of tout — simplement don’t utiliser a custom font. That’s the whole ladder.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.