Type something to search...
How to Preload Web Fonts the Right Way?

How to Preload Web Fonts the Right Way?

A teammate reads that preloading fonts improves performance, so they add preload tags for every font file in the project: regular, italic, medium, semibold, bold, and two weights of a heading face. Lighthouse scores drop. The hero image, which is the Largest Contentful Paint element, now loads later, because seven font files are fighting it for bandwidth in the first second. Chrome's console fills with warnings that resources were "preloaded but not used within a few seconds."

Preloading fonts is genuinely useful, but only when you do it precisely. This article explains why fonts benefit from preloading, the exact syntax that works (and the one attribute nearly everyone forgets), how to choose which files to preload, and how to do it in Next.js, WordPress, and through HTTP headers.

Why Fonts Are Discovered Late

Browsers do not download fonts when they see an @font-face rule. They download a font only when they know some visible text needs it. That requires:

  1. Downloading and parsing the HTML.
  2. Downloading and parsing the CSS that contains the @font-face rules.
  3. Building the render tree and matching elements to font families, weights, and styles.
  4. Requesting the font file.

Only at step 4 does the font request start. On a mobile connection, that can be several hundred milliseconds after the HTML arrived. During that time, text is either invisible or shown in a fallback, producing the flashes described in our guide to FOUT vs. FOIT.

Preloading tells the browser about the font in the HTML head, so the request starts during step 1, in parallel with the CSS.

What rel=preload Does

A preload is a high-priority fetch hint. It says: "This page will definitely need this resource soon; download it now." The browser puts the file in its memory cache, and when the CSS later asks for the same font, it is already there or already on its way.

Preloading does not apply the font. It does not change font-display. It only moves the download earlier. That is why a preload for a font the page never uses is pure waste.

The Correct Syntax

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

Every attribute matters:

AttributePurposeWhat happens if it is missing
rel="preload"Marks this as a preload hintNothing is preloaded
hrefMust exactly match the URL in your @font-face srcA second download when the CSS requests a different URL
as="font"Sets the request destination and priorityThe preload is ignored or fetched with wrong priority
type="font/woff2"Lets browsers skip formats they do not supportMinor; all modern browsers support WOFF2 anyway
crossoriginFetches in CORS anonymous mode, matching how fonts are always fetchedThe preloaded file is not reused, and the font downloads twice

Why crossorigin Is Required Even on Your Own Domain

Font requests triggered by @font-face are always made in CORS mode, even when the font is on the same origin. A preload without crossorigin is made in no-CORS mode. The browser treats them as two different requests, so the preloaded copy sits unused and the font is fetched again. You will see the file twice in the Network panel and get a console warning. Always add crossorigin (which is equivalent to crossorigin="anonymous") to font preloads.

Which Fonts to Preload

Preload only the fonts that render above-the-fold text on first paint. For most sites that is one file, sometimes two.

  1. The body text font, regular weight. It renders the most text, often including the LCP element.
  2. The heading font, if different, when the hero headline is the LCP element.
  3. Nothing else. Italic, bold, and secondary weights can load normally.

A practical budget: one to two font preloads, totaling under about 100KB. A variable font helps here, since one file covers every weight you use above the fold.

What Not to Preload

  • Fonts used only below the fold, in footers, or in modals.
  • Every unicode-range subset. Preload the subset that matches your page's language, such as latin, not latin-ext, cyrillic, and greek as well.
  • Icon fonts, unless icons are critical to the first screen. SVG icons are usually a better choice anyway.
  • Fonts from third-party hosts whose URLs you do not control, since the URL can change and break the match.

Preload vs. Preconnect

If you cannot self-host and must load fonts from a third-party origin, you often cannot preload because the final file URL is not known in advance. In that case, use preconnect to set up the connection early:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

The crossorigin on the fonts.gstatic.com preconnect matters for the same reason as above: the font requests will be CORS requests, so the warmed-up connection must be a CORS connection.

HintWhat it doesUse for fonts when
preloadDownloads a specific file earlyYou know the exact URL (self-hosted)
preconnectOpens DNS, TCP, and TLS to an origin earlyFonts come from a third-party host
dns-prefetchResolves DNS onlyA low-cost fallback for less critical origins

The best option is to remove the third-party origin entirely by self-hosting your fonts, which makes preload possible.

How Preload Interacts with font-display

Preloading and font-display work together:

  • With swap, preloading shortens the time the fallback is visible, so fewer readers see a swap at all.
  • With optional, preloading is close to essential. The font is only used if it arrives within roughly 100ms, and a preload makes that much more likely on first visits. Chromium also gives preloaded optional fonts a chance to render on the first paint, avoiding both swap and layout shift.
  • With block, preloading reduces the time text stays invisible.

See our guide on which font-display value to use for the full timeline.

A Note on Priority and fetchpriority

Font preloads are fetched at high priority by default. If your LCP element is an image, too many high-priority font preloads can delay it. You can lower a font's priority with fetchpriority="low", though at that point it is worth asking whether the preload is needed at all. For the hero image, add fetchpriority="high" so it competes on equal terms.

<link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin />
<img src="/images/hero.avif" alt="Dashboard overview" fetchpriority="high" width="1200" height="630" />

Preloading in Next.js

next/font preloads fonts automatically. It adds a preload link for each font file used by the route, with crossorigin set correctly, and it only preloads the subsets you list.

import { Inter, Playfair_Display } from "next/font/google";

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-inter",
});

const playfair = Playfair_Display({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-playfair",
  preload: false, // used only below the fold, so skip the preload
});

The preload option defaults to true. Set it to false for secondary fonts. Because the subsets array controls which files are preloaded, listing only "latin" avoids preloading scripts you do not use.

If you load font files yourself, React 19 provides a preload function that works in Server Components and dedupes automatically:

import { preload } from "react-dom";

export default function Page() {
  preload("/fonts/inter-latin.woff2", {
    as: "font",
    type: "font/woff2",
    crossOrigin: "anonymous",
  });

  return <main>...</main>;
}

Preloading in WordPress

Add the preload link in your theme's functions.php with the wp_head hook, using a priority that places it early:

add_action( 'wp_head', function () {
    $font_url = get_theme_file_uri( 'assets/fonts/inter-latin.woff2' );
    printf(
        '<link rel="preload" href="%s" as="font" type="font/woff2" crossorigin>' . "\n",
        esc_url( $font_url )
    );
}, 1 );

Make sure the URL is exactly the one your theme.json or stylesheet references, including any version query string. A ?ver= parameter on one but not the other causes a double download.

Preloading with HTTP Headers and Early Hints

You can also send a preload as an HTTP Link header, which the browser sees before it parses any HTML:

Link: </fonts/inter-latin.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin

Some CDNs, including Cloudflare, can turn these headers into 103 Early Hints responses, which tell the browser to start fetching while the server is still generating the page. That is the earliest possible moment a font download can begin.

Verifying Your Preloads

  1. Open DevTools, select the Network panel, filter by "Font", and disable the cache.
  2. Reload and confirm each preloaded font appears once, with the Initiator column showing the document rather than the stylesheet.
  3. Check the Console for "preloaded using link preload but not used within a few seconds" (unused preload) or a credentials mode mismatch warning (missing crossorigin).
  4. In the Performance panel, check that font downloads start in parallel with the CSS, not after it.
  5. Compare LCP before and after with a throttled mobile profile. If LCP got worse, you are preloading too much.

Preloading Web Fonts FAQ

Fonts are always fetched in CORS mode, even from the same origin. A preload without crossorigin is fetched in a different mode, so the browser cannot reuse it and downloads the font a second time. Adding crossorigin makes the two requests match.

Usually one or two. Preload only the files that render text visible on first paint, typically the regular weight of the body font and possibly the hero heading font. Preloading more competes with images and CSS and can make the page slower.

Not reliably from the hosted API, because the file URLs are generated by Google and can change. Use preconnect for the Google domains instead, or self-host the fonts so you know the exact URLs and can preload them.

Yes. It adds preload links for the font files and subsets you configure, with the correct attributes. You can turn this off for individual fonts with the preload option set to false.

Chrome shows it when a preloaded resource was not requested by the page within a few seconds. Either the font is not used above the fold, the URL does not match the @font-face src exactly, or the crossorigin attribute is missing.

Yes, if the variable font renders above-the-fold text. A single variable file often replaces several static weights, which makes it an efficient preload because one request covers every weight in the hero section.

Conclusion

Preloading fixes a real problem: fonts are discovered late because the browser needs the CSS and the render tree before it knows which files to request. A preload moves that request to the start of the page load, shrinking the window where text is invisible or shown in a fallback.

The rules are short. Preload only the one or two files that render above-the-fold text, match the href exactly to your @font-face URL, always include as="font", type="font/woff2", and crossorigin, and self-host so the URLs are stable. Then verify in DevTools that each font downloads once and early, and that your LCP improved rather than regressed.

Here are some useful references for going deeper on preloading fonts:

  1. MDN Web Docs: rel=preload — syntax, the as attribute, and CORS-enabled fetches.
  2. web.dev: Preload critical assets to improve loading speed — when preloading helps and how to measure it.
  3. web.dev: Best practices for fonts — how preload, preconnect, and font-display fit together.
  4. Next.js Docs: Font optimization — automatic font preloading and the preload option in next/font.
  5. React Docs: preload — the React DOM API for preloading resources from components.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading