Preload Hint for Critical Web Fonts With crossorigin
Issue No: 116
Category: Resource Hints & Performance Issue Type: Warning Priority: Important
Description
Pages that use custom web fonts should declare <link rel="preload" as="font" crossorigin> tags for those fonts so the browser fetches them early and eliminates Flash of Invisible Text (FOIT) caused by fonts not being available when text first renders.
The XeoPix crawler detects web font usage by inspecting inline <style> blocks for @font-face rules and checking for <link> tags pointing to known font CDN origins (e.g., fonts.googleapis.com, fonts.gstatic.com, fonts.bunny.net, use.typekit.net). The trigger for missing font preloads only fires when the page shows evidence of web font usage.
How Do We Capture It
Where to Find
Inside HTML <head>:
<link rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-regular.woff2"
crossorigin>
The crossorigin attribute is required even for same-origin fonts when using rel="preload" for font resources.
HTML Extraction Flow
The XeoPix crawler downloads the raw HTML response and parses the <head> block:
- Issues an HTTP GET to the target URL.
- Reads raw response bytes without JavaScript execution.
- Scans the
<head>block for all<link rel="preload">tags. - Filters for tags where the
asattribute equalsfont. - Extracts the
href,type, and presence of thecrossoriginattribute from each matched tag. - Records all font preload hrefs and whether each declares
crossorigin. - Sets
font_preload_has_crossorigintoFALSEif any font preload tag is missing the attribute. - Scans all inline
<style>blocks in the page for@font-facerules; setshas_inline_font_face = TRUEif found. - Scans all
<link rel="stylesheet">tags for hrefs matching known font CDN hostnames (fonts.googleapis.com,fonts.gstatic.com,fonts.bunny.net,use.typekit.net,cloud.typography.com); setshas_known_font_cdn_link = TRUEif found. - Sets
has_web_font_signals = TRUEif eitherhas_inline_font_faceorhas_known_font_cdn_linkis TRUE. - Sets
has_web_fonts_but_no_preload = TRUEifhas_web_font_signals = TRUE AND has_font_preload = FALSE.
Validation Logic
The crawler validates:
- Whether any
<link rel="preload" as="font">tag exists. - Whether every matched font preload tag includes the
crossoriginattribute. - Whether the
hrefis a valid non-empty URL. - Whether inline
<style>blocks contain@font-facerules. - Whether any
<link>tag points to a known font CDN hostname.
How to Get It
Crawler should:
- Parse raw HTML
<head>. - Find all:
link[rel="preload"][as="font"]. - Extract
href,type, and whethercrossoriginattribute is present for each. - Count matched tags; flag if any are missing
crossorigin. - Scan all inline
<style>blocks for@font-facekeyword. - Scan all
<link rel="stylesheet">hrefs for known font CDN hostnames. - Set web font signal flags based on findings from steps 5 and 6.
What to Store
| Field | Type | Comment |
|---|---|---|
has_font_preload | BOOLEAN | Whether any font preload link tag is declared in the page head |
font_preload_hrefs | TEXT[] | Array of font href values declared in preload tags |
font_preload_count | INT | Number of font preload link tags found on the page |
font_preload_has_crossorigin | BOOLEAN | Whether all font preload tags include the crossorigin attribute |
font_preload_missing_crossorigin_count | INT | Number of font preload tags that are missing the crossorigin attribute |
has_inline_font_face | BOOLEAN | Whether any inline style block contains an @font-face rule |
has_known_font_cdn_link | BOOLEAN | Whether any link stylesheet points to a known font CDN hostname |
has_web_font_signals | BOOLEAN | Whether the page shows any scriptable evidence of web font usage |
has_web_fonts_but_no_preload | BOOLEAN | Whether web font signals exist but no font preload hint is declared |
Condition for Trigger
The following trigger rules evaluate font preload presence (guarded by web font detection) and crossorigin compliance.
-
Trigger Rule 1: Web Fonts Detected But No Font Preload Declared
- Target Field:
has_web_fonts_but_no_preload - Evaluation Logic:
= TRUE - Severity: WARNING
- Diagnostic Message: "This page shows evidence of web font usage (inline @font-face or a known font CDN link) but declares no font preload hint. Without a preload hint, fonts are not discovered until CSS is fully parsed, causing Flash of Invisible Text (FOIT). Add a preload hint for each critical font file."
- Target Field:
-
Trigger Rule 2: Font Preload Missing crossorigin Attribute
- Target Field:
font_preload_missing_crossorigin_count - Evaluation Logic:
> 0 - Severity: WARNING
- Diagnostic Message: "One or more font preload tags are missing the crossorigin attribute. Without crossorigin, the browser initiates a second separate fetch for the font file, causing the preload to be wasted and FOIT to persist."
- Target Field:
Sources
- MDN — link rel=preload for fonts
- web.dev — Preload web fonts to improve loading speed
- web.dev — Prevent layout shifts and FOIT/FOUT
- W3C Preload specification — CORS and preload
- Google Fonts — Optimize font loading
Long Description
Web fonts are one of the most common sources of Flash of Invisible Text (FOIT) — the condition where text on a page is invisible while the font is downloading. This happens because browsers wait for a font to load before rendering text that uses it.
The standard font loading path without preload:
- Browser downloads HTML.
- Browser downloads CSS (render-blocking).
- CSS is parsed; browser discovers
@font-facerule referencing/fonts/inter.woff2. - Only now does the browser request the font file.
- Text is invisible until the font arrives — FOIT.
With <link rel="preload" as="font" crossorigin> in <head>:
- Browser parses the preload tag immediately on HTML download.
- Font fetch begins at the highest priority, in parallel with CSS.
- By the time CSS is parsed and text needs to render, the font is already available.
- FOIT is eliminated or drastically reduced.
Why crossorigin is required:
Font resources are always fetched using anonymous CORS mode by the @font-face rule in CSS. The preload hint must match the same CORS mode as the actual fetch. Without crossorigin on the preload tag:
Preload request: same-origin mode
Actual @font-face request: CORS anonymous mode
→ Browser cannot reuse the preloaded response → second download initiated
→ Preload hint is completely wasted
Example correct font preload:
<link rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-v13-regular.woff2"
crossorigin>
Common failure scenarios:
1. Font Preload Without crossorigin
<!-- BAD: crossorigin missing -->
<link rel="preload" as="font" href="/fonts/inter.woff2">
<!-- GOOD -->
<link rel="preload" as="font" href="/fonts/inter.woff2" crossorigin>
2. Preloading the Wrong Font Format
Preloading .ttf when the browser would prefer .woff2 means the preloaded file may never be used, and the preferred format is fetched late.
3. Preloading All Fonts Instead of Critical Ones
Preloading 10 font files (all weights and styles) delays the highest-priority resources. Only preload the one or two font files used in above-the-fold text.
4. Preload Href Mismatch With @font-face src
If the preload href does not exactly match the URL in the @font-face src declaration, the browser treats them as different resources and downloads both.
How to Fix
Add <link rel="preload" as="font" crossorigin> for each critical web font file in the page <head>:
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter-regular.woff2" crossorigin>
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter-bold.woff2" crossorigin>
Guidelines:
- Always include the
crossoriginattribute — it is required for all font preloads regardless of origin. - Include the
typeattribute matching the font format (font/woff2,font/woff) to allow browsers that do not support the format to skip the preload. - Only preload fonts used in above-the-fold text — limit to 2–3 files maximum.
- The
hrefmust exactly match the URL in the corresponding@font-facesrcdeclaration. - Place font preload tags early in
<head>, before any<link rel="stylesheet">tags.