---
title: "Why Fonts Need crossorigin: Preload and CORS"
description: "Why font preloads need crossorigin even on your own domain, how a missing one makes fonts download twice, the CDN headers to send, and how to check it."
image: "https://www.blackseoanalyzer.com/static/images/black-seo-analyzer-og-image.png"
canonical: "https://www.blackseoanalyzer.com/en/blog/font-crossorigin"
language: "en"
---

# Why Fonts Need crossorigin: Preload, CORS and the font-crossorigin Warning

By [Seth Black](https://www.blackseoanalyzer.com/en/about) · Published November 01, 2026

Fonts need `crossorigin` because browsers always fetch web fonts in anonymous-mode CORS, even when the font is on your own domain. A `<link rel="preload" as="font">` without the attribute is a different kind of request from the one the CSS makes, so the browser can’t reuse it, and the font downloads twice. Add `crossorigin` to every font preload, and to the `preconnect` for any font host.

## Why do fonts need crossorigin?

The CSS Fonts specification’s font fetching requirements make every `@font-face` download a CORS request in anonymous mode. Images and stylesheets don’t work this way; fonts do, which is why this catches people who’ve preloaded other assets without trouble.

MDN’s [preload reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload) states it directly: `font` and `fetch` preloading requires the `crossorigin` attribute, and the attribute “needs to be set to match the resource’s CORS and credentials mode, even when the fetch is not cross-origin.”

A preload is only used if it matches the later request: same URL, same destination (`as`), same CORS mode. Without `crossorigin`, the preload is a no-CORS request; the CSS then asks for the font in CORS mode, finds no matching preload, and fetches it again. web.dev’s [preload guide](https://web.dev/articles/preload-critical-assets) puts it in one line: “Fonts preloaded without the `crossorigin` attribute will be fetched twice.”

So the missing attribute doesn’t just waste the preload. It doubles the font’s bandwidth and still leaves the text waiting.

## What’s the correct markup?

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

Each attribute has a job:

| Attribute | Value | Why |
| --- | --- | --- |
| `rel` | `preload` | Fetch early, before the CSS asks. |
| `as` | `font` | Sets the request’s destination and priority. Without it the preload won’t match. |
| `type` | `font/woff2` | Browsers that can’t use the format skip the download. |
| `crossorigin` | empty or `anonymous` | Matches the anonymous CORS mode the CSS will use. |

A bare `crossorigin` and `crossorigin="anonymous"` mean the same thing. Don’t use `crossorigin="use-credentials"` for fonts; the font request is made without credentials, so a credentialed preload won’t match either.

Preload only the one or two fonts the first screen renders with. Preloading every weight competes with the HTML, CSS and hero image for bandwidth.

## What about Google Fonts and other font CDNs?

When fonts come from another origin, two more things apply.

**Preconnect with crossorigin.** web.dev’s guide to [preconnect](https://web.dev/articles/preconnect-and-dns-prefetch) says fonts are loaded in anonymous mode, so “you must set the `crossorigin` attribute with the `preconnect` hint”, and without it “the browser only performs the DNS lookup.” For Google Fonts that means:

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

The stylesheet comes from `fonts.googleapis.com` (no CORS, no attribute); the font files come from `fonts.gstatic.com` (CORS, attribute required).

**The font server must allow your origin.** A cross-origin font is only used if the response carries `Access-Control-Allow-Origin`. Google Fonts sends it. If you host fonts on your own CDN hostname, you have to send it yourself.

nginx:

```nginx
location ~* \.(?:woff2|woff)$ {
    add_header Access-Control-Allow-Origin "https://www.example.com" always;
    add_header Cache-Control "public, max-age=31536000, immutable" always;
}
```

Apache:

```apache
<FilesMatch "\.(woff2|woff)$">
  Header set Access-Control-Allow-Origin "https://www.example.com"
</FilesMatch>
```

Use `*` instead of a specific origin if several sites share the font host. If you use a specific origin and serve more than one, send `Vary: Origin` so caches don’t hand one site’s header to another.

## How do you tell if a font is being downloaded twice?

Open DevTools, go to Network, filter by Font, and reload with the cache disabled. If the same `.woff2` appears twice, the preload didn’t match. Chrome also logs a Console warning when a preload goes unused, saying the preload was found but not used because the request’s credentials mode doesn’t match, and pointing you at the `crossorigin` attribute.

From the command line, check the raw HTML for font preloads missing the attribute:

```bash
curl -s https://example.com/ | grep -io '<link[^>]*as="font"[^>]*>' | grep -iv crossorigin
```

Any line this prints is a font preload without `crossorigin`. To confirm a cross-origin font host sends the CORS header:

```bash
curl -s -D - -o /dev/null -H 'Origin: https://www.example.com' \
  https://cdn.example.com/fonts/inter-var.woff2 | grep -i access-control-allow-origin
```

No output means the browser will refuse the font, and the page falls back to a system font after a wasted download.

## What does the font-crossorigin warning mean in bseoa?

bseoa checks every `<link rel="preload" as="font">` on every page it crawls and reports the ones without a `crossorigin` attribute. The warning appears as [font crossorigin](https://www.blackseoanalyzer.com/en/technical-seo/performance-analyzer/font-crossorigin) from the performance analyzer and as [font missing crossorigin](https://www.blackseoanalyzer.com/en/technical-seo/web-vitals-analyzer/font-missing-crossorigin) from the Core Web Vitals analyzer; they’re the same finding seen from two angles, and one fix clears both. The same check also reports font preloads that aren’t WOFF2.

What it doesn’t check: `@font-face` rules inside your CSS files, and whether a font host sends `Access-Control-Allow-Origin`. Use the `curl` commands above for those.

The fix usually lives in one template, the `<head>` partial, so one change clears the warning on every page. To find every page carrying it, [bseoa’s website crawler](https://www.blackseoanalyzer.com/en/website-crawler) checks the whole site in one run and lists each page with its warnings.

## Why does this matter for SEO?

Indirectly, through speed. A font fetched twice delays text rendered in that font, and when the largest element on the first screen is a heading or paragraph, that’s your Largest Contentful Paint. [How to diagnose Core Web Vitals failures at the page level](https://www.blackseoanalyzer.com/en/blog/how-to-diagnose-core-web-vitals-failures-at-the-page-level) walks through finding which element is the LCP. Font preloads are also a line on the [pre-deployment SEO checklist for developers](https://www.blackseoanalyzer.com/en/blog/the-pre-deployment-seo-checklist-for-developers), because a template change is the usual way the attribute goes missing.

## Quick answers

**Do same-origin fonts need crossorigin?** On a preload, yes. That’s the whole point of this post.

**Does `@font-face` need a crossorigin attribute?** No. There’s no attribute in CSS; the browser applies CORS to font loads automatically. Only the HTML preload and preconnect hints need it.

**Is `crossorigin="anonymous"` different from `crossorigin`?** No. They’re identical.

**Can I fix it with a header instead?** No. The preload’s mode comes from the HTML attribute. The server header only decides whether a cross-origin font is allowed.

-Sethers

About the author

[Seth Black](https://www.blackseoanalyzer.com/en/about)

Seth is a software engineer and engineering leader who builds bseoa, a Rust-based technical SEO crawler with a GUI and CLI. He writes about the crawling, rendering and indexing problems he runs into on real sites.

[LinkedIn](https://www.linkedin.com/in/seth-black-tx/) · [GitHub](https://github.com/sethblack) · [YouTube](https://www.youtube.com/@SethBlack)

[Back to Blog](https://www.blackseoanalyzer.com/en/blog)

## Discover hundreds of SEO Issues in Seconds

Without Monthly Subscriptions

Comprehensive technical SEO analysis powered by ML and 16 specialized modules. Optional AI-powered insights from Claude, GPT-4, or Gemini. Get actionable insights in seconds, and never pay monthly fees again.

[Download Free Trial](https://www.blackseoanalyzer.com/en/free-trial)

I use AI to generate images for my posts and for general editing, updates, and ironically SEO purposes. I used to draw all of the images for my personal blog (taleas) myself, but as the volume of content I produce has increased, I've turned to AI tools to help create visuals that complement my writing. I go out of my way to generate images that look strange, and don't represent real people. If you ever want to chat about my use of AI, please reach out.

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://www.blackseoanalyzer.com/#organization","name":"Fiscus Technology, LLC","url":"https://www.blackseoanalyzer.com/","logo":{"@type":"ImageObject","url":"https://www.blackseoanalyzer.com/static/images/black-seo-analyzer.png"},"sameAs":["https://sethserver.com/","https://github.com/sethblack"],"founder":{"@id":"https://www.blackseoanalyzer.com/#seth"}},{"@type":"WebSite","@id":"https://www.blackseoanalyzer.com/#website","name":"bseoa","url":"https://www.blackseoanalyzer.com/","publisher":{"@id":"https://www.blackseoanalyzer.com/#organization"}},{"@type":"Person","@id":"https://www.blackseoanalyzer.com/#seth","name":"Seth Black","url":"https://www.blackseoanalyzer.com/about","jobTitle":"Creator of bseoa","sameAs":["https://www.linkedin.com/in/seth-black-tx/","https://github.com/sethblack","https://www.youtube.com/@SethBlack","https://sethserver.com/"]},{"@type":"BlogPosting","@id":"https://www.blackseoanalyzer.com/en/blog/font-crossorigin#article","headline":"Why Fonts Need crossorigin: Preload, CORS and the font-crossorigin Warning","name":"Why Fonts Need crossorigin: Preload, CORS and the font-crossorigin Warning","description":"Why font preloads need crossorigin even on your own domain, how a missing one makes fonts download twice, the CDN headers to send, and how to check it.","url":"https://www.blackseoanalyzer.com/en/blog/font-crossorigin","datePublished":"2026-11-01T05:21:24","dateModified":"2026-11-01T05:21:24","author":{"@id":"https://www.blackseoanalyzer.com/#seth"},"publisher":{"@id":"https://www.blackseoanalyzer.com/#organization"},"image":{"@type":"ImageObject","url":"https://www.blackseoanalyzer.com/static/images/black-seo-analyzer-og-image.png","width":1200,"height":630},"mainEntityOfPage":{"@type":"WebPage","@id":"https://www.blackseoanalyzer.com/en/blog/font-crossorigin"},"isPartOf":{"@id":"https://www.blackseoanalyzer.com/#website"},"inLanguage":"en-US","isAccessibleForFree":true,"wordCount":1018},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://www.blackseoanalyzer.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://www.blackseoanalyzer.com/en/blog"},{"@type":"ListItem","position":3,"name":"Why Fonts Need crossorigin: Preload, CORS and the font-crossorigin Warning","item":"https://www.blackseoanalyzer.com/en/blog/font-crossorigin"}]}]}
```
