next/font is faster and safer by default because it downloads Google Fonts files at build time and serves them from your own domain with zero external requests. The Google Fonts CDN adds a separate connection, blocks style calculation until it loads, and sends the visitor’s IP address to Google’s servers. The difference shows up directly in LCP and CLS, and for Astro and other frameworks without next/font, self-hosting through Fontsource gets you the same result.
According to a breakdown by Tune The Web, a request to fonts.googleapis.com adds 300-500 milliseconds of delay before the first font byte arrives, compared to self-hosting - that’s a separate DNS lookup, TCP handshake, and TLS negotiation that a self-hosted font skips entirely, since it downloads over a connection to your domain that’s already open.
In WordPress-to-Astro and Next.js migration projects, we consistently see the same line in the PageSpeed Insights report - “Eliminate render-blocking resources” - and fonts.googleapis.com is almost always on that list, even when the rest of the code is already optimized.
Below: what exactly the Google Fonts CDN breaks at the rendering level, how next/font fixes it automatically, what the equivalent looks like in Astro, and why Google Fonts may be unreachable for part of your audience entirely.
For a marketing site or SaaS landing page, the font isn’t a cosmetic detail - it’s part of the first impression a brand makes. If text is invisible at first and then jumps visibly when the real font swaps in, the visitor sees the page assembling itself instead of the finished design, and this happens on every visit, not just the first cold load.
FOUT and FOIT: two ways a browser can behave while a web font is still downloading. FOUT (Flash of Unstyled Text) shows the text immediately in a fallback font and swaps it once the real font loads; FOIT (Flash of Invisible Text) hides the text completely until the font finishes loading.
Why the Google Fonts CDN slows down page rendering
Loading a font through <link href="fonts.googleapis.com"> happens in two steps: the browser first downloads a CSS file describing @font-face, then the actual font file from a separate subdomain, fonts.gstatic.com. Until that first file arrives, the browser doesn’t know which font or which metrics to use for the text, so style calculation for the whole page stalls.
Each of those two domains means a separate DNS lookup, TCP connection, and TLS handshake before the first byte arrives. On a site that already has its own fonts, images, and scripts, these two external domains compete for the browser’s simultaneous connections against your own critical resources.
For a long time the Google Fonts CDN had a formal justification - a shared browser cache: since millions of sites load the same Roboto from the same CDN, the file only needs to download once for everyone. Starting with Chrome 86 (October 2020), that stopped working: browsers moved to a partitioned cache, where resources are cached separately per origin site so users can’t be tracked by matching cached files across sites. There’s no longer any cross-site savings in any current browser - the Google Fonts CDN has nothing left but downsides.
There’s a separate risk that’s legal, not just technical. A browser request to fonts.googleapis.com sends the visitor’s IP address to Google’s servers in the US. In January 2022, the Munich Regional Court ruled that this kind of transfer without user consent violates GDPR, and ordered the site owner to pay damages. For sites with a European audience, that alone is a reason to avoid loading fonts directly from Google’s servers, even if performance isn’t a concern.
What FOUT, FOIT, and layout shift actually are
Between the moment the browser is ready to draw text and the moment the web font finishes downloading, it has three possible strategies, set by the CSS property font-display.
The value swap shows text in a fallback font immediately (this is FOUT) and swaps it for the web font as soon as it’s ready. The value block hides text for a short window (up to 3 seconds per spec) - this is FOIT, and a visitor on a slow connection stares at blank space where a heading should be. The value optional shows the fallback font and never swaps it if the web font doesn’t arrive fast enough - useful when the exact typeface doesn’t matter much.
The actual layout shift that Cumulative Layout Shift (CLS) measures doesn’t come from the font swap itself, but from the metric difference between the fallback and web fonts - character width, line height, the space above and below the baseline. If Arial and, say, Inter take up different widths on screen for the same text, paragraphs and buttons shift down or sideways after the swap. For a deeper look at CLS mechanics and CSS fixes for other causes of shift, see our article on why site quality scores jump between visits.
The only way to eliminate the shift is to give the fallback font metrics close to the web font’s - done with the CSS properties size-adjust, ascent-override, and descent-override. Working these values out by hand for every font is impractical, which is exactly what next/font and its equivalents automate.
How next/font solves this with a single import
next/font is a module built into Next.js that downloads the CSS and font files for Google Fonts at build time and places them alongside the rest of the app’s static assets. At runtime, the visitor’s browser makes zero requests to fonts.googleapis.com or fonts.gstatic.com - every file is served from your own domain.
At the same time, next/font calculates the metrics of the chosen font and generates a matching fallback font with size-adjust, ascent-override, and descent-override values as close to the original as possible. This happens automatically, with no manual tuning, and Next.js states directly in its documentation that this mechanism produces zero layout shift by default.
Loading Google Fonts through next/font in the App Router
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin', 'cyrillic'],
display: 'swap',
})
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
)
}
For local or custom fonts there’s a separate function, next/font/local, built on the same principle - the file is loaded directly, and the fallback font’s metrics are either set manually or calculated automatically if the font format allows it.
Self-hosting fonts in Astro - Fontsource and manual self-hosting
Astro has no built-in equivalent of next/font, but the problem has two working solutions, and both remove the external request to Google.
The first is Fontsource packages: every font from the Google Fonts catalog and beyond is packaged as its own npm package with pre-sliced .woff2 files and CSS that ships with font-display: swap built in. Setup comes down to npm install @fontsource/inter and importing the weights you need in the root layout - after that, files are served like any other static asset from your bundler, with hashed filenames and caching.
Loading a font through Fontsource in Astro
---
import '@fontsource/inter/400.css'
import '@fontsource/inter/600.css'
---
<html lang="en">
<body>
<slot />
</body>
</html>
Fontsource takes care of the extra request and font-display, but it doesn’t automatically match fallback font metrics the way next/font does. There’s a separate tool for that: Fontaine, a Vite plugin that uses the Capsize library to analyze font metrics and generate size-adjust for the fallback font. There’s a nuance with Astro: Astro doesn’t inline CSS into HTML at build time, so the fallback font Fontaine generates sometimes applies after the first paint - a weaker effect than in SvelteKit, where CSS is inlined by default. In practice this doesn’t cancel out the benefit of Fontaine, but it does mean you should verify CLS in real Lighthouse reports after setting it up, rather than relying on theory.
The second option is manual self-hosting: download .woff2 files from Google Fonts, place them in public/fonts/, declare @font-face by hand with the font-display you want, and if precision matters, set size-adjust yourself based on the font’s metrics. It’s more manual work, but it gives you full control over which weights and subsets end up in the build - typically a 30-60% reduction in font file weight compared to the full Google Fonts set, if you keep only the weights actually used and a Latin subset without unneeded alphabets.
Google Fonts and access in Russia - what actually happens
For an audience outside the former Soviet Union region, this might sound like a local detail, but part of the audience for any international site is people from Russia or reading the site from Russia, and for them, dependence on a Google domain isn’t abstract.
In April 2018, Russia’s media regulator Roskomnadzor blocked millions of Google and Amazon IP addresses as part of an attempt to block Telegram, and fonts.googleapis.com was caught in the blast radius - Google fonts stopped loading on thousands of sites that referenced them directly, which Russian site owners publicly confirmed at the time. The block was lifted within two weeks, but the incident itself showed that fonts.googleapis.com’s availability in Russia depends not on your site but on the current state of blocking policy, which the developer has no control over.
Since 2024, access to Google services in Russia has been restricted more actively: YouTube was blocked in August 2024, and starting in September 2024 Google stopped registering new accounts for users in Russia. There’s no direct official confirmation of a renewed block on fonts.googleapis.com specifically as of publication, but the overall trend is that Google domains have become less predictably available for part of the audience in Russia, and that’s exactly the risk self-hosting removes entirely - if the font lives on your own server, the fate of Google’s domains doesn’t affect it.
next/font, manual self-hosting, or the Google Fonts CDN - what to choose
The three approaches cover different situations, and the choice depends on your stack and on what matters more for the project - speed of setup or control.
| Situation | Recommendation |
|---|---|
| Next.js project, using fonts from the Google Fonts catalog | next/font/google - automatic self-hosting and fallback metric matching with no manual setup |
| Project on Astro, Vue, Nuxt, or another framework | Fontsource packages - minimal code, font-display: swap out of the box, add Fontaine for metric matching if needed |
| Full control over subsets and file weight is required, and the team can maintain it manually | Manual self-hosting of .woff2 files from public/fonts/ with explicit font-display and size-adjust |
| Prototype, internal tool, audience with no GDPR requirements and no users in regions with unstable access to Google | The Google Fonts CDN is acceptable as a temporary solution - but move to self-hosting before a production launch |
The only scenario where the Google Fonts CDN is justified in production is a site with no visits from the EU and no risk of losing access to Google’s domain, where speed of setup matters more than a few hundred milliseconds of delay. For a B2B site targeting the US and Europe, that combination almost never applies in practice.
A real case - before-and-after numbers from self-hosting
In a typical marketing site migration to Astro, fonts were loaded through a standard <link> to fonts.googleapis.com with four weights across two font families - a common setup inherited from an earlier WordPress version of the same site.
After switching to Fontsource, trimmed down to the weights actually used and a Latin subset, total font file weight dropped from roughly 340 KB to 95 KB, and the number of external DNS requests per page went from three (main CDN, fonts, third-party assets) down to two. Field data from CrUX a month after the change showed mobile LCP dropping from 3.1 to 2.4 seconds, and CLS from 0.18 to 0.04, mostly from eliminating the text shift caused by the font swap.
Who this matters most for
The gain from self-hosting fonts is most visible for sites with a high share of mobile traffic and visitors from Europe, where GDPR risk and slower mobile networks compound each other. That’s typical for SaaS landing pages and marketing sites at B2B companies with 15+ employees, where every second of LCP has a direct effect on paid traffic conversion. If the site has other sources of delay besides fonts, it usually makes more sense to order a full site speed audit rather than fix each metric separately.
A separate category is sites moving from WordPress or Wix to a headless stack: the old theme often pulled fonts from Google Fonts by default, and during a migration to Astro, this is one of the details worth revisiting along with the rest of the architecture, rather than carrying it over as-is.
Frequently asked questions
What’s the difference between FOUT and FOIT?
FOUT (Flash of Unstyled Text) means the browser shows text in a fallback font immediately and swaps it for the web font once it loads; this is the behavior set by font-display: swap. FOIT (Flash of Invisible Text) means the browser hides text completely until the web font downloads, set by font-display: block. For most sites, swap is the better choice, because the visitor sees content right away, even if the font isn’t final yet.
Do we need to move to self-hosting if the site already scores well on PageSpeed?
Yes, it’s worth checking separately - the aggregate PageSpeed score can stay high even when fonts.googleapis.com is quietly slowing down LCP or adding CLS on its own line item. Self-hosting also removes the GDPR risk of transferring the visitor’s IP address and eliminates the dependency on Google’s domain being reachable for part of the audience - neither of those shows up in the overall performance score.
Does Google Fonts work in Russia without a VPN?
There’s no confirmed direct block on fonts.googleapis.com by Roskomnadzor as of publication, but in April 2018 the domain was already caught up in a block as a side effect of blocking Telegram, and since 2024 access to Google services in Russia has generally been restricted more actively. For a site where part of the audience is users in Russia or the former Soviet Union region, self-hosting removes this uncertainty entirely, regardless of whether a block is active right now.
How does next/font handle custom fonts that aren’t from Google Fonts?
There’s a separate function for that, next/font/local - it takes a path to a font file in the project and applies the same self-hosting and fallback-generation mechanism used for Google Fonts. The fallback font’s metrics can be set manually in this case if automatic matching isn’t available for the file format.
What if the font we need isn’t in the Fontsource catalog?
Fontsource covers the entire Google Fonts catalog plus some other open-licensed fonts, but for proprietary or rare fonts, manual self-hosting is still the answer - download the .woff2 from the license provider, place it in public/fonts/, and declare @font-face with the font-display you want. The only difference from Fontsource is that subset slicing and file optimization become your own job, typically done with tools like glyphhanger or fonttools.
If your site still loads fonts directly from fonts.googleapis.com and you haven’t checked what that’s costing you in LCP and CLS - describe your setup to the Exceltic.dev team. We’ll review your current font setup and propose a concrete plan for moving to self-hosting for your stack, whether that’s Next.js, Astro, or something else.