FontSelf Blog
Core Web Vitals and Fonts: How Self-Hosting Improves Your LCP Score
Fonts are one of the most consistent sources of Core Web Vitals problems on the web. Not because fonts are inherently slow, but because the default way most developers load them — a <link>tag pointing at Google's CDN — introduces a chain of dependencies that delays text rendering in ways that are entirely avoidable.
This article explains exactly what happens in the browser when you load Google Fonts the standard way, which Core Web Vitals metrics are affected and why, and how self-hosting resolves each problem at the root rather than patching around it.
What Core Web Vitals Actually Measure
Google's Core Web Vitals are three metrics that measure real user experience:
- LCP — Largest Contentful Paint.The time from navigation start until the largest visible content element — usually a heading, hero image, or paragraph of text — finishes rendering. Google's threshold for a "Good" rating is under 2.5 seconds.
- CLS — Cumulative Layout Shift.The total amount of unexpected layout movement during the page's lifetime. Text that renders in a fallback font and then shifts when the web font loads contributes to CLS.
- INP — Interaction to Next Paint. Replaced FID in March 2024. Measures responsiveness to user input. Fonts have minimal direct impact on INP.
For most content-heavy sites, fonts are a direct input to both LCP and CLS. LCP is affected because the largest content element is often a text heading, and that heading cannot render correctly until the font is available. CLS is affected by the layout shift that occurs when a fallback font is swapped for the web font mid-render.
The CDN Loading Waterfall
When you include the standard Google Fonts embed in your HTML, this is what happens in the browser before your text renders:
Step 1 — DNS lookup for fonts.googleapis.com
The browser has never seen this domain before on the user's first visit. It must query the DNS system to resolve the IP address. This typically takes 20–120ms on a fast connection. On a mobile connection or with a slow DNS resolver, it can exceed 200ms.
Step 2 — TCP connection + TLS handshake to fonts.googleapis.com
Once the IP address is resolved, the browser opens a TCP connection and negotiates TLS. On a first visit this adds another 100–300ms depending on network latency and server location.
Step 3 — HTTP request for the CSS file
The browser fetches the CSS file from fonts.googleapis.com. This file contains the @font-face declarations listing the actual WOFF2 file URLs. The browser did not know these URLs existed until this request completed — it could not begin fetching the font files any earlier.
Step 4 — DNS lookup for fonts.gstatic.com
The WOFF2 files are served from a different domain: fonts.gstatic.com. Another DNS lookup. Another 20–120ms.
Step 5 — TCP connection + TLS handshake to fonts.gstatic.com
Another connection. Another handshake.
Step 6 — HTTP request for each WOFF2 file
Finally, the actual font binary downloads. For a latin-only subset at a single weight, this is typically 70–120KB.
Step 7 — Font parsing and text render
The browser parses the WOFF2 file and renders the text.
Each step in this chain is sequential. Step 4 cannot begin until Step 3 completes. Step 6 cannot begin until Step 5 completes. On a fast broadband connection with a warm DNS cache this chain might complete in 400–600ms total. On a mobile connection with cold cache it can easily exceed 1,500ms.
If the largest content element on your page is a text heading — which it is on most content sites, blogs, and marketing pages — every millisecond of this chain is directly added to your LCP measurement.
The preconnect Partial Fix and Why It Is Not Enough
Google's own documentation recommends adding rel="preconnect" hints to reduce the latency of the CDN waterfall:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>This tells the browser to begin the DNS lookup and TCP/TLS handshake for both domains early, before it encounters the actual stylesheet link. In practice, preconnect reduces the connection overhead from Steps 1–2 and Steps 4–5 to near zero if the hints are processed early enough.
What preconnect cannot do is eliminate Steps 3 and 6 — the actual HTTP requests for the CSS file and the WOFF2 binaries. The CSS request still has to complete before the browser knows which WOFF2 URLs to fetch. The font binary still has to download from an external server. The waterfall is shortened but not eliminated.
Preconnect also depends on the hints being processed before the browser encounters the stylesheet link. If your <head> is large or the hints appear after other blocking resources, the benefit diminishes. On slow connections where DNS and connection overhead are highest, the hints are often processed too late to fully close the gap.
Preconnect is a workaround for a problem that self-hosting eliminates entirely.
How Self-Hosting Changes the Waterfall
When font files are served from your own domain, the entire external connection chain disappears. The browser already has a connection open to your domain — it is the connection it used to fetch the HTML. The font files are just additional resources on the same origin.
The waterfall for a self-hosted font looks like this:
Step 1 — HTTP request for the WOFF2 file
On the same connection already open to your server. No DNS lookup. No new TCP connection. No TLS handshake. Just the request and the download.
Step 2 — Font parsing and text render
Done.
On a cold first visit, the difference between the CDN waterfall and the self-hosted waterfall is typically 200–600ms. On mobile connections where DNS resolution and connection setup are slower, the difference is larger — often 500ms to over 1,000ms.
For a page where the LCP element is a text heading, this is a direct, measurable improvement to your LCP score. You are not optimizing around the problem. You are removing it.
The Self-Hosting Multiplier
Self-hosting unlocks a technique that is not available with CDN-loaded fonts: rel="preload".
<link
rel="preload"
href="/fonts/inter-variable.woff2"
as="font"
type="font/woff2"
crossorigin
>A preload hint tells the browser to fetch the specified resource at the highest priority, immediately, before it has parsed the CSS that references it. For fonts this is significant because normally the browser does not know a font is needed until it has parsed the CSS and determined which elements use it. By the time the browser discovers the font, it may already be several hundred milliseconds into rendering the page.
With a preload hint, the font download begins in parallel with the HTML parse. On a fast connection, the WOFF2 file may already be fully downloaded and cached by the time the browser first needs to render text that uses it. The font is never a blocker.
This technique requires knowing the font URL at HTML generation time — which is only possible when you control the file location. With Google's CDN you cannot add a preload for the WOFF2 file because you do not know its URL until after the CSS has been fetched and parsed. The URL includes version hashes and subset identifiers that change when Google updates the font. Self-hosting gives you a stable, known URL you can hardcode into your <head>.
The CLS Connection
Cumulative Layout Shift measures layout instability. Fonts contribute to CLS through the flash of unstyled text or the shift that occurs when a fallback system font is replaced by the web font mid-render.
The font-display: swap setting — which FontSelf uses as the default — tells the browser to render text immediately in the fallback font and then swap to the web font when it is available. This eliminates invisible text, but the swap itself can cause a layout shift if the web font and fallback font have different metrics — different x-height, different character widths, different line height.
Self-hosting reduces the window in which the fallback font is visible by making the web font available faster. A font that loads in 150ms from your own CDN-cached server gives the layout shift much less time to occur than a font that loads in 800ms from an external CDN waterfall. In many cases, on a fast connection, the font is available before the first paint and no swap occurs at all.
For fonts with significant metric differences from system fallbacks — display typefaces, wide or condensed designs — you can additionally use the CSS size-adjust, ascent-override, descent-override, and line-gap-override descriptors to make the fallback font metrics match the web font. This eliminates the layout shift entirely even when a swap does occur. This is an advanced technique, but self-hosting is the prerequisite — you need stable font metrics and a controlled loading environment to tune overrides reliably.
font-display: optional for Maximum LCP
If LCP is your primary concern and you are willing to accept that the web font may not render on the first visit, font-display: optional is the most aggressive optimization available.
With optional, the browser gives the font a very short loading window — roughly 100ms. If the font is available within that window, it is used. If it is not, the browser falls back to the system font for the rest of the page lifetime and does not perform a swap. On the second visit, the font is served from cache within the 100ms window and renders immediately.
The tradeoff is that first-time visitors may see your page in a system font. For sites where brand consistency is critical, this is unacceptable. For sites where LCP score is the primary optimization target — landing pages, content sites competing in PageSpeed benchmarks — optional combined with self-hosting and a preload hint produces the best possible Core Web Vitals outcome. The preload hint maximizes the chance the font is available within the 100ms window even on first visit.
FontSelf lets you set font-display to optional in the download configuration. The generated CSS uses the correct value and the ZIP contains everything needed for immediate deployment.
Measuring the Improvement
After deploying self-hosted fonts, measure the impact before and after using:
Chrome DevTools — Network tab
Filter by Font. Confirm zero requests to fonts.gstatic.com or fonts.googleapis.com. Note the timing of the WOFF2 request — it should now appear much earlier in the waterfall and complete faster.
Chrome DevTools — Performance tab
Record a page load. Look for the LCP marker in the timeline. Compare the timestamp before and after self-hosting. A reduction of 200–600ms is typical on a desktop connection. On a throttled mobile profile the reduction is often larger.
PageSpeed Insights
Run your URL through PageSpeed Insights before and after. Look specifically at the "Eliminate render-blocking resources" and "Reduce the impact of third-party code" audit results — both should clear after self-hosting. The LCP field data takes 28 days to update in the CrUX dataset, so lab data from PageSpeed is the faster feedback loop immediately after deployment.
WebPageTest
Use the filmstrip view to see exactly when text becomes visible. Self-hosting typically moves the first text render earlier in the timeline by a visible amount, especially on the mobile 4G profile.
Summary
Loading Google Fonts from the CDN introduces a six-step sequential waterfall that adds 200–1,500ms to font availability depending on network conditions, and all of that time is directly added to your LCP score when the largest content element is text. Self-hosting eliminates the external connection chain entirely, reduces font loading to a single same-origin request, and unlocks rel="preload" which allows the font to download in parallel with HTML parsing before the browser has even discovered it is needed. The result is a direct, measurable improvement to LCP with no architectural complexity — download the ZIP from FontSelf, place the files on your server, add the preload hint, and remove the CDN link.