> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Font Auditor

> Checks how a page loads its web fonts - font-display, WOFF2, hosting, preloads, fallback metrics and file size

<Info>
  **Performance Auditor** — checks that web fonts show text immediately, ship in the smallest format, come from the page's own host and stay small.
</Info>

<CardGroup cols={2}>
  <Card title="Performance" icon="zap">
    Fonts are on the critical path of every first visit. The wrong setup hides text, opens extra connections and downloads hundreds of kilobytes nobody reads.
  </Card>

  <Card title="Per family and per file" icon="list-checks">
    Findings name the font family, the stylesheet that declares it and the file that is too large, so you know exactly what to change.
  </Card>
</CardGroup>

This auditor reads the `@font-face` rules a page declares and its font preload links, then checks them against the checklist of the Browser Diet article [Web Fonts Without the Flash](https://browserdiet.com/web-fonts-without-the-flash-font-display-subsetting-and-fallbacks): WOFF2 only, self-hosted, at most two families (variable when possible), `font-display: swap` or `optional`, metric-compatible fallbacks, one preloaded font with `crossorigin`, and subsetted files.

## How it works

1. **Fetch the page** — requests the audited URL (redirects followed). A network failure or non-2xx status produces an `ERROR` response and nothing else is evaluated.
2. **Collect the CSS** — reads every inline `<style>` block and `style` attribute, downloads every linked stylesheet (including the `rel="preload" as="style"` pattern), and follows `@import` one level deep. Up to 30 stylesheets, 10 at a time, 10-second timeout each.
3. **Parse the font rules** — extracts every `@font-face` rule: family, `font-display`, the sources of the last `src` descriptor with their formats, weight, style, `unicode-range` and metric overrides (`size-adjust`, `ascent-override`, `descent-override`, `line-gap-override`). Font preload links (`<link rel="preload" as="font">`) are collected from the HTML.
4. **Keep what the browser downloads** — a rule is judged only when its family is referenced by CSS outside `@font-face` (or its file is preloaded), its first supported source is a downloadable file, and its `unicode-range` covers at least one character of the page text. Declared but unreferenced families are listed in the check data as `unusedFamilies` and are not judged.
5. **Measure the font files** — asks for the size of each downloaded font file with `HEAD` (falling back to `GET`), up to 20 files.
6. **Aggregate** — any `FAIL` rule makes the audit `FAIL`; otherwise any `WARNING` rule makes it `WARNING`; when every rule passes the audit is `SUCCESS`.

Icon fonts (Font Awesome, Material Icons and Symbols, Dashicons and similar, recognised by family name) are exempt from the `font-display`, family-count and fallback-metric rules, because hiding icon glyphs until the font arrives is the correct behaviour. They are still checked for WOFF2, hosting and file size.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`. CheckCode suffixes use status ranges: `200` success, `300–399` warning, `400–499` fail, `500–599` error. Rules `400`–`402` and `302` run once per font family, `300` once per font host, `303` once per preload link, `306` once per font file and `307`–`308` once per stylesheet.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-WebFontAuditor-400" />`37A-WebFontAuditor-400` | `FAIL` | A text font family the page uses has `@font-face` rules with no `font-display` descriptor. | Add font-display: swap (or optional) to the `{n}` @font-face rule(s) for `"{family}"` in `{stylesheet}`. For a Google Fonts stylesheet: Append `&display=swap` to the Google Fonts URL `{stylesheetUrl}` so the `{n}` @font-face rule(s) for `"{family}"` show fallback text while the font downloads. | Without font-display the browser hides text for up to three seconds while the font downloads. Visitors on slow connections stare at a blank page and many leave before reading. |
| <a id="37A-WebFontAuditor-401" />`37A-WebFontAuditor-401` | `FAIL` | A text font family the page uses declares `font-display: block` or `auto`. `swap`, `optional` and `fallback` pass. | Change font-display: `{value}` to swap or optional for `"{family}"` in `{stylesheet}`. | This setting tells the browser to hide text until the font has downloaded. On slow connections visitors see blank space where the content should be, and many leave. |
| <a id="37A-WebFontAuditor-402" />`37A-WebFontAuditor-402` | `FAIL` | A font family the page uses has rules whose downloadable sources are all of a known format and none is WOFF2. | Convert `{fontFile}` to WOFF2 and list it first in the src of `"{family}"` in `{stylesheet}`. | WOFF2 is the smallest font format and every current browser supports it. Older formats are noticeably larger, so each new visitor waits longer before text looks right. |
| <a id="37A-WebFontAuditor-300" />`37A-WebFontAuditor-300` | `WARNING` | Font files are downloaded from a host other than the page's host. | Download the `{families}` files from `{fontHost}` and serve them from `{pageHost}` so the browser does not open a second connection. | Fonts on another host force the browser to look up, connect to and secure a second server before the download can start. That delay lands on the first visit, when impressions are made. |
| <a id="37A-WebFontAuditor-301" />`37A-WebFontAuditor-301` | `WARNING` | The page uses more than 2 text font families. | Reduce the `{n}` families (`{families}`) to 2 or fewer, for example one for headings and one for body text. | Every extra family adds downloads that compete with content and images. Pages with many typefaces load slower, most noticeably on mobile connections. |
| <a id="37A-WebFontAuditor-302" />`37A-WebFontAuditor-302` | `WARNING` | A text font family loads 4 or more distinct static files for its weights and styles within one `unicode-range`, and none of its rules is a variable font. | Replace the `{n}` static `"{family}"` files (`{variants}`) with one variable WOFF2 file. | Each weight or style in its own file is another request and more bytes. One variable font file covers all of them and usually weighs less in total. |
| <a id="37A-WebFontAuditor-303" />`37A-WebFontAuditor-303` | `WARNING` | A `<link rel="preload" as="font">` has no `crossorigin` attribute. | Add crossorigin to `<link rel="preload" href="{href}" as="font">`. | Fonts are always requested in anonymous mode, so a preload without crossorigin does not match and the browser downloads the file twice. That wastes bandwidth and delays everything queued behind it. |
| <a id="37A-WebFontAuditor-304" />`37A-WebFontAuditor-304` | `WARNING` | The page preloads more than 1 font. | Keep one of the `{n}` font preloads (`{preloads}`), the headline font, and remove the rest. | Preloaded files jump the queue ahead of stylesheets and images. Preloading several fonts slows the content visitors came for instead of speeding it up. |
| <a id="37A-WebFontAuditor-305" />`37A-WebFontAuditor-305` | `WARNING` | A text font family swaps in (its `font-display` is not `optional`) and no `@font-face` on the page sets `size-adjust`, `ascent-override`, `descent-override` or `line-gap-override`. | Declare a fallback @font-face for `{families}` (src: local('Arial') with size-adjust, ascent-override and descent-override) and list it after the web font in font-family. | When the web font replaces the fallback, lines change length and the page jumps. Readers lose their place and the layout shift counts against Core Web Vitals. |
| <a id="37A-WebFontAuditor-306" />`37A-WebFontAuditor-306` | `WARNING` | A measured font file is larger than the 100 KB budget. | Reduce `{fontFile}` (`{size}` KB): subset it to the characters the site uses, for example Latin plus punctuation, which typically cuts 60–80%, and drop variable axes the design does not use. | A font file this large usually carries alphabets and symbols the site never shows. Visitors pay for those bytes on the first visit, which is slowest on mobile data. |
| <a id="37A-WebFontAuditor-307" />`37A-WebFontAuditor-307` | `WARNING` | Fetching a stylesheet returned a non-2xx HTTP status, so the fonts it declares could not be audited. | Fix the reference to `{stylesheet}` (HTTP `{statusCode}`) or remove it from the page, then re-run the audit. | A stylesheet that answers with an error is also failing for visitors, so the page may render with the wrong fonts or unstyled. |
| <a id="37A-WebFontAuditor-308" />`37A-WebFontAuditor-308` | `WARNING` | A stylesheet could not be fetched at all (timeout, DNS, TLS, connection refused), so the fonts it declares could not be audited. | Make `{stylesheet}` reachable (last error: `{error}`) or remove it from the page, then re-run the audit. | A stylesheet that cannot be downloaded is likely failing for visitors too, leaving the page with the wrong fonts or unstyled. |
| <a id="37A-WebFontAuditor-500" />`37A-WebFontAuditor-500` | `ERROR` | The audited page itself could not be fetched or returned a non-2xx status. | Verify `{url}` is reachable and returns HTTP 200, then re-run the audit. Last error was `{error}`. | — |
| <a id="37A-WebFontAuditor-200" />`37A-WebFontAuditor-200` | `SUCCESS` | All rules passed. Also emitted when the page uses no web fonts. | — | — |

Every message quotes what was observed, for example: `Font family "Inter" has 2 @font-face rule(s) without font-display in /css/site.css, so its text stays invisible while the font downloads.` or `Font file /fonts/inter.woff2 ("Inter") weighs 313 KB, above the 100 KB budget.`

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | Every web font rule passed, or the page uses no web fonts | No `300`–`308` and no `400`–`402` finding; one `200` check whose message lists the families, the number of `@font-face` rules judged and the largest font file. |
| **WARNING** | Text stays visible and fonts are WOFF2, but the setup can be leaner | No `400`–`402` finding, and at least one of: third-party font host (`300`), more than 2 families (`301`), 4 or more static files in a family (`302`), preload without `crossorigin` (`303`), more than 1 font preload (`304`), no fallback metric overrides (`305`), font file over 100 KB (`306`), unreadable stylesheet (`307`, `308`). |
| **FAIL** | A font hides text while it loads, or is not served as WOFF2 | A used family has no `font-display` (`400`), uses `block` or `auto` (`401`), or has no WOFF2 source (`402`). The summary message is the first finding in that order. |
| **ERROR** | The auditor could not fetch the page | Network failure, invalid URL or non-2xx status on the audited URL (`500`). |

## Risks and considerations

### Invisible text loses readers

* Without `font-display`, browsers hide text for up to three seconds while a font downloads. On a slow phone connection that is three seconds of a blank page.
* `swap` shows the fallback font at once and swaps when the web font arrives. `optional` uses the web font only when it is already cached, which is the calmest choice for body text.

### Every extra file and host is paid on the first visit

* WOFF2 is the smallest font format and all current browsers support it; older formats are larger for no benefit.
* Fonts on another host need a new DNS lookup, connection and TLS handshake, because font requests never reuse the connection that fetched the stylesheet.
* A font preload without `crossorigin` is thrown away and the file is downloaded twice. Several preloads delay the stylesheets and images behind them.
* Subsetting to the characters a site uses typically cuts a font file by 60–80%. One variable font file can replace four or more static weight files.

### Layout shift when fonts swap

* A fallback font with different metrics makes lines reflow when the web font arrives. A fallback `@font-face` tuned with `size-adjust`, `ascent-override` and `descent-override` keeps the text in place.

### What this auditor does not check

* **Fonts declared by JavaScript.** Only CSS present in the HTML, in linked stylesheets and one level of `@import` is read. Styles injected at runtime are not seen.
* **Whether a CSS rule matches an element.** A family counts as used when any CSS rule references it, even if that rule matches nothing on the page. Themes that define many font presets can therefore report more families than a visitor downloads.
* **Proof of subsetting.** File size is the only signal: a file over 100 KB is reported, but the glyphs inside it are not inspected. A variable font with several axes can exceed the budget even when subsetted.
* **Legacy fallbacks after WOFF2.** A `src` that lists WOFF2 first and older formats after it passes; the extra entries cost nothing in current browsers.
* **More than 20 font files or 30 stylesheets.** Larger pages are sampled in document order.
* Stylesheets and fonts on other hosts are requested without the page's Basic Auth credentials. Caching of font files is covered by the Cache Auditor, and compression by the Content Encoding Auditor.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.