Poorly configured web fonts are one of the most common causes of slow LCP and high CLS: the browser blocks text while downloading the font file, and when the font finally arrives, the text shifts size and pushes the layout around. Both problems are solved with three targeted adjustments to your CSS and HTML.
This guide covers exactly what to do: the right format (WOFF2), the font-display directive, preloading the critical font, and trimming file size with subsetting.
Why Web Fonts Hurt LCP and CLS
Google measures page performance through Core Web Vitals. Fonts directly affect two of them:
- LCP (Largest Contentful Paint): if the largest element on screen is a text block using an external font, LCP cannot fire until that font finishes loading.
- CLS (Cumulative Layout Shift): when the browser first renders a fallback font and then swaps in the web font, the text may grow or shrink in height and push other elements around. That movement adds to CLS.
The impact is measurable: a 150 KB font file can add 400–800 ms to LCP on mobile connections. A poorly handled font swap can push CLS above Google's "Poor" threshold (0.25).
Step 1 — Always Use WOFF2
WOFF2 delivers 30–50 % better compression than WOFF and is supported by all modern browsers (Chrome 36+, Firefox 39+, Safari 12+). If you are still serving TTF, OTF, or legacy WOFF, you are sending unnecessary kilobytes on every page load.
In your @font-face declaration, list only WOFF2 with a WOFF fallback for very old browsers:
@font-face {
font-family: 'MyFont';
src: url('/fonts/my-font.woff2') format('woff2'),
url('/fonts/my-font.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
If you use Google Fonts, append &display=swap to the URL and request only the font weights you actually use:
https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap
Step 2 — Set font-display Correctly
The font-display property controls what the user sees while the font downloads. It is the single most important setting for LCP and CLS:
| Value | Behavior | LCP/CLS Impact |
|---|---|---|
auto | Browser decides (usually blocks) | Hurts LCP |
block | Text invisible until font loads | Badly hurts LCP |
swap | Immediate fallback, then swap when ready | Improves LCP; can hurt CLS |
fallback | 100 ms wait, then fallback; swaps if font loads quickly | Good balance |
optional | Brief wait; if not loaded, uses fallback permanently | Best for CLS; sacrifices custom typography |
For most sites: use font-display: swap combined with font metric overrides (see Step 4) to control CLS.
For secondary or decorative fonts that are not part of the LCP element: use font-display: optional. If the font does not arrive in time, the browser skips it for that visit and avoids any layout shift.
Step 3 — Preload the Critical Font with <link rel="preload">
If your font is part of the LCP element (a large heading or paragraph), tell the browser to fetch it as early as possible by adding this line in your HTML <head>, before your stylesheets:
<link rel="preload"
href="/fonts/my-font-regular.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous">
Key points:
- Preload a maximum of 1 or 2 files. Preloading every font weight saturates the connection and worsens overall performance.
- The
crossorigin="anonymous"attribute is required for font files, even when they are hosted on the same domain. - For Google Fonts or any external CDN, the preload only helps if you also add a
<link rel="preconnect">to the origin domain.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Step 4 — Eliminate CLS with Font Metric Overrides
With font-display: swap, the browser first displays the fallback font (Arial, Georgia…) and then swaps in the web font. If the two fonts have different metrics, the text shifts — CLS.
CSS now provides properties to adjust the fallback font's metrics so they visually match the web font:
@font-face {
font-family: 'MyFont-Fallback';
src: local('Arial');
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
size-adjust: 107%;
}
body {
font-family: 'MyFont', 'MyFont-Fallback', Arial, sans-serif;
}
The exact percentages depend on each font pair. Tools like Font Style Matcher (meowni.ca) or the Fontaine CLI calculate them automatically. This technique can bring font-related CLS close to zero.
For more performance strategies, explore the performance category with guides on images, caching, and Core Web Vitals. If you would rather delegate the work, elenlace.com offers full Core Web Vitals audits with implementation included.
Step 5 — Reduce File Size with Subsetting
A complete font file can include support for Cyrillic, Greek, Vietnamese, and dozens of character sets you will never need on an English or Spanish site. Subsetting removes those unnecessary glyphs.
With Google Fonts, request only the Latin subset:
https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap&subset=latin
If you self-host, use the pyftsubset tool from fonttools:
pyftsubset my-font.ttf \
--unicodes="U+0020-007F,U+00C0-00FF,U+00A1,U+00BF" \
--output-file="my-font-latin.ttf" \
--flavor=woff2
The unicode range covers standard ASCII plus the extended Latin characters needed for Spanish and English (accents, ñ, ¡, ¿). The result is typically 60–80 % smaller than the full font file.
Key Takeaways
- Use WOFF2 exclusively; remove TTF, OTF, and legacy WOFF from your stack.
font-display: swapimproves LCP by showing text immediately with a fallback font.- Preload only the LCP font file with
<link rel="preload">placed in the<head>before stylesheets. - CSS
ascent-override,descent-override, andsize-adjustalign the fallback font with the web font and eliminate CLS. - Subsetting reduces file size by 60–80 % by stripping glyph sets you never use.
Want to implement these improvements but short on time? Contact the team at elenlace.com — we handle complete font and Core Web Vitals optimization so you can focus on your business.
FAQ
Is it better to self-host fonts or use Google Fonts?
Self-hosting is generally faster because it eliminates the external connection and gives you full control over cache headers. Since 2022, Chrome no longer shares the Google Fonts cache across sites, removing that historic advantage. The exception: if your own server is slow, Google Fonts may be faster thanks to its global CDN.
How many fonts can I use without hurting performance?
The practical rule is a maximum of 2 font families and 2–3 weights per family. Each font file is an additional HTTP request. With WOFF2 and subsetting, a well-optimized font can weigh under 20 KB; four weights of a single poorly configured family can exceed 400 KB.
Does font preloading work in all browsers?
<link rel="preload"> is supported in modern Chrome, Edge, Firefox, and Safari (covering more than 95 % of users). In browsers that do not support it, the tag is simply ignored without causing any errors — it never blocks page load.
How do I know if my fonts are causing CLS?
Open Chrome DevTools → Performance tab → enable "Web Vitals" and record a page load. CLS events appear marked on the timeline. You can also use PageSpeed Insights or Google Search Console's Core Web Vitals report to identify pages with elevated CLS caused by text shifts.
Useful resources
Other providers and guides worth comparing: