Type something to search...
How to preload fonts correctly?

How to preload fonts correctly?

To preload a font correctly, add a link element with rel="preload", as="font", type="font/woff2" and the crossorigin attribute to the head of your HTML, with an href that exactly matches the URL used in your @font-face rule. The crossorigin attribute is required even when the font is on your own domain, because browsers always fetch fonts in CORS mode, and without it the preloaded file can't be reused and the font downloads twice. Preload only the one or two font files used by text visible when the page first loads, usually the main body or heading font. Preloading tells the browser to start the download immediately instead of waiting until it has parsed your CSS and worked out which fonts the page needs.

Fonts are among the last resources a browser discovers on a typical page, which is why they so often arrive after the first paint and cause a flash or a layout shift. A correct preload fixes that discovery delay, but an incorrect one wastes bandwidth or does nothing at all. In this article you'll learn why fonts are discovered late, the exact syntax and what each attribute does, how to choose which files to preload, how to preload with HTTP headers and in popular frameworks, and how to check your preloads are working.

Why Fonts Need Preloading

Without a preload, the browser finds a font only after several steps:

  1. It downloads the HTML and finds a stylesheet link.
  2. It downloads and parses the CSS, which contains @font-face rules.
  3. It builds the render tree and discovers that visible text needs a particular face.
  4. Only then does it request the font file.

Even with fast servers, each step adds latency. If the stylesheet itself is imported from another stylesheet or injected by JavaScript, the font is discovered later still.

A preload moves the font request up to step one. As soon as the browser's preload scanner reads the head, it starts the download in parallel with the CSS, so the font is often ready by the time the first paint happens.

The Correct Syntax

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Example</title>

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

  <link rel="stylesheet" href="/css/main.css">
</head>

And the matching rule in main.css:

@font-face {
  font-family: "Figtree";
  src: url("/fonts/figtree-latin-var.woff2") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

What Each Attribute Does

  • rel="preload": Tells the browser to fetch the resource now, at high priority, without applying it.
  • href: The font URL. It must match the src URL in @font-face exactly, after resolution. A different query string, host or path means a separate request.
  • as="font": Sets the request's destination and priority, and lets the browser match the preloaded response to the later font request. Without it, the preload is fetched as a generic resource and won't be reused.
  • type="font/woff2": Lets browsers that don't support the format skip the download. All current browsers support WOFF2, but it's still good practice.
  • crossorigin: Makes the preload a CORS request in anonymous mode, which is how fonts are always fetched. Without it, the preload and the real font request have different modes and the browser downloads the file twice.

Place preloads early in the head, before stylesheets and scripts, so the browser sees them as soon as possible.

The crossorigin Gotcha

This is by far the most common mistake. It's easy to assume crossorigin is only needed for fonts on other domains, but the Fonts specification requires all font loads to use CORS. A preload without the attribute is a non-CORS request, and the browser can't use its response for a CORS font load.

Chrome warns about this in the console:

A preload for 'https://www.example.com/fonts/figtree-latin-var.woff2' is found,
but is not used because the request credentials mode does not match.
Consider taking a look at crossorigin attribute.

If you serve fonts from another origin, such as a CDN subdomain, the server must also send an Access-Control-Allow-Origin header that covers your page's origin.

Which Fonts to Preload

Preloading raises a resource's priority, which means something else gets less bandwidth. Preload too many fonts and you delay your CSS, hero image or scripts, and the page gets slower.

Pick fonts using these rules:

  1. Preload fonts used above the fold: Usually the body text font, and the heading font if the main headline is visible on load.
  2. Preload the face that's actually used: If your hero heading is bold, preload the bold file, not the regular one. With a variable font, one file covers all weights.
  3. Preload the right subset: If you split a font by unicode-range, preload only the subset your main language needs, such as Latin.
  4. Don't preload fonts used only further down: Italic, bold italic, footer fonts and code fonts can load normally.
  5. Stop at one or two: For most sites that's enough. Beyond that, the benefit falls away quickly.

How to Find the Right Files

Open DevTools, disable the cache, and reload with Network filtered by Font. The files that load during the initial render are your candidates. To see which element uses which face, inspect your main headline and first paragraph and check Rendered Fonts in the Computed tab.

Preloading With an HTTP Header

You can send a preload as a Link response header instead of an HTML element. This lets your server or CDN add preloads without changing templates, and the browser sees them before it starts parsing the HTML:

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

In Nginx:

location = / {
    add_header Link '</fonts/figtree-latin-var.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin';
    try_files $uri /index.html;
}

Some CDNs can turn Link preload headers into 103 Early Hints responses, which the browser receives while the server is still generating the page. For dynamically rendered pages with slow server response times, that gives the font download an even earlier start.

Preloading in Frameworks

Next.js

next/font self-hosts fonts and adds preload links automatically for the fonts used on each route. Preloading is on by default and can be turned off per font:

import { Figtree } from "next/font/google";

export const figtree = Figtree({
  subsets: ["latin"],
  display: "swap",
  preload: true,
});

Only the subsets you list are preloaded, so keep subsets to what the page actually needs.

Vite and Other Bundlers

If your bundler fingerprints font filenames, you can't hard-code the URL. Import the font as an asset and inject the preload with the hashed URL. In a Vite project:

import figtreeUrl from "./fonts/figtree-latin-var.woff2?url";

const link = document.createElement("link");
link.rel = "preload";
link.as = "font";
link.type = "font/woff2";
link.href = figtreeUrl;
link.crossOrigin = "anonymous";
document.head.appendChild(link);

Adding a preload from JavaScript is later than writing it in the HTML, so prefer server-rendered templates or a build plugin that writes the link into index.html when you can.

WordPress

In a classic or block theme, you can output a preload from functions.php:

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

The priority of 1 places it near the top of the head. Make sure your @font-face rule or theme.json points to the same URL.

Preload, Preconnect and fetchpriority

These hints solve different problems:

HintWhat it doesUse for fonts when
preloadFetches a specific file earlyYou know the exact font URL
preconnectOpens a connection to an origin earlyFonts are on another origin and the URL may change, such as Google Fonts
fetchpriorityAdjusts the priority of a requestRarely needed; preloaded fonts are already high priority

If your fonts are on your own domain, you don't need preconnect at all. If you load Google Fonts, preconnect to fonts.gstatic.com with crossorigin rather than preloading file URLs, because Google may change those URLs when it updates a family.

Checking Your Preloads Work

  1. One request per font: In the Network panel, each preloaded font should appear once. Two rows for the same file means a mismatch in URL or crossorigin.
  2. Early start: In the Waterfall, the font request should start at about the same time as your CSS, not after it.
  3. Initiator: Hover the font request's Initiator column. A correct preload shows the HTML document as the initiator.
  4. No console warnings: Chrome warns about preloads that weren't used within a few seconds of the load event and about credential mode mismatches.

A quick way to list preloads on a live page:

[...document.querySelectorAll('link[rel="preload"][as="font"]')].forEach((l) =>
  console.log(l.href, l.crossOrigin ?? "missing crossorigin")
);
https://www.example.com/fonts/figtree-latin-var.woff2 anonymous

Common Mistakes

  • Missing crossorigin: Causes a double download.
  • Mismatched URLs: For example, preloading /fonts/a.woff2 while the CSS uses https://cdn.example.com/fonts/a.woff2.
  • Preloading every weight: Bandwidth goes to fonts that may not even be used on the page.
  • Preloading fonts not used on this page: Site-wide templates often preload a font only one section of the site needs.
  • Preloading WOFF and WOFF2: Only the format the browser uses should be preloaded, which today is WOFF2.
  • Forgetting font-display: Preloading speeds up the download but doesn't stop invisible text if the download is still slow. Pair it with the right font-display value.

FAQ: Preloading Fonts

Because browsers fetch every font in CORS mode, regardless of origin. A preload without crossorigin uses a different request mode, so the browser can't reuse it and downloads the font again.

Usually one or two: the files used by visible text when the page first loads. Preloading more takes bandwidth from CSS and images and can make the page slower overall.

Generally no. Google's font file URLs can change when a family is updated. Preconnect to fonts.gstatic.com instead, or self-host the font so you can preload a stable URL.

It shortens it, because the font arrives sooner, and often removes it on fast connections. On slow networks the fallback may still show briefly, so combine preloading with font-display and a matched fallback.

Chrome logs it when a preloaded resource isn't used within a few seconds of the page loading. For fonts, it usually means the page doesn't use that face, or the URL or crossorigin attribute doesn't match.

Yes, and it's often ideal, because a single variable file can cover every weight used above the fold. Preload it exactly as you would a static WOFF2 file.


Conclusion

Preloading fixes the main reason fonts arrive late: the browser doesn't know it needs them until it has parsed your CSS and laid out the page. A single link element with rel="preload", as="font", type="font/woff2", crossorigin and an exactly matching URL starts the download straight away, alongside your stylesheet.

Keep it disciplined. Preload only the one or two files that render visible text on load, double-check the crossorigin attribute, and confirm in the Network panel that each font downloads once and starts early. Combined with a sensible font-display value, that's usually enough to make font loading invisible to most visitors.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper