Performance & Maintenance

How to Optimize Web Fonts to Improve LCP and Prevent CLS

Optimize web fonts with font-display, preload, and subsetting to improve LCP scores and eliminate the layout shift that tanks your CLS.

A laptop displaying an analytics dashboard with real-time data tracking and analysis tools.

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:

ValueBehaviorLCP/CLS Impact
autoBrowser decides (usually blocks)Hurts LCP
blockText invisible until font loadsBadly hurts LCP
swapImmediate fallback, then swap when readyImproves LCP; can hurt CLS
fallback100 ms wait, then fallback; swaps if font loads quicklyGood balance
optionalBrief wait; if not loaded, uses fallback permanentlyBest 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.

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: swap improves 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, and size-adjust align 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:

← All