WOFF2 Explained: WOFF vs WOFF2, Compression, and crossorigin
By Seth Black · Published
WOFF2 (Web Open Font Format 2.0) is a compressed font file format made for the web. It wraps the same TrueType or OpenType font data as a .ttf or .otf file, compressed with Brotli after a font-specific preprocessing step, so the same font downloads faster. Every modern browser supports it, and for most sites it’s the only font format you need to serve.
What is WOFF2, exactly?
WOFF2 is a W3C Recommendation; the current version of the WOFF 2.0 specification is dated 8 August 2024. Two things make it smaller than its predecessor:
- Preprocessing. Before compressing, it transforms the font’s tables. The
glyftable, which holds glyph outlines, is split into separate streams that group similar data together, and thelocatable is rebuilt from it on decode rather than stored. - Brotli. The spec uses Brotli for both the font data and the metadata block. WOFF 1.0 used zlib (the same compression as gzip).
The MIME type is font/woff2 and the file extension is .woff2.
WOFF vs WOFF2: what’s the difference?
| WOFF (1.0) | WOFF2 | |
|---|---|---|
| Compression | zlib (Flate) per table | Font-aware preprocessing plus Brotli |
| Size vs WOFF 1.0 | Baseline | About 25% smaller on average |
| Browser support | All browsers, including IE9 to IE11 | All modern browsers |
| MIME type | font/woff |
font/woff2 |
| Use today | Only as a fallback for very old browsers | Default |
The size figure comes from the W3C’s WOFF 2.0 evaluation report, which compressed the Google Fonts collection both ways: WOFF2 was 25.11% smaller than WOFF 1.0 on average, with a range of 11.93% to 55.32% depending on the font. Google’s web.dev guide to reducing web font size puts it as “up to 30% better compression than WOFF”.
Does WOFF2 matter for SEO?
Fonts don’t rank. What they do is sit on the critical path: text that uses a web font can’t render in that font until the file arrives. Smaller fonts mean less time with invisible or swapping text, which shows up in Largest Contentful Paint when the largest element is a heading or paragraph, and in layout shift when the fallback font has different metrics. If you’re working through those metrics, how to diagnose Core Web Vitals failures at the page level covers where fonts fit in. Font format is also one line on the pre-deployment SEO checklist for developers, which is the right place to catch it before a redesign ships.
How do you use WOFF2?
Serve WOFF2 in your @font-face rule. For nearly every site in 2026, one source is enough:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
If you genuinely need Internet Explorer 11, list WOFF2 first and WOFF second; browsers use the first format they support:
src: url("/fonts/inter.woff2") format("woff2"),
url("/fonts/inter.woff") format("woff");
For the one or two fonts the first screen needs, preload them, with crossorigin:
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
Leave out crossorigin and, as web.dev’s preload guide puts it, the font will be “fetched twice”, because fonts are always fetched in CORS mode and a preload without the attribute doesn’t match the real request. That’s its own warning, font crossorigin.
How do you convert a font to WOFF2?
With Google’s reference encoder (google/woff2):
woff2_compress Inter-Regular.ttf # writes Inter-Regular.woff2
woff2_decompress Inter-Regular.woff2 # back to .ttf
With fontTools, which also lets you subset to the characters you use, often a bigger saving than the format change itself:
pip install fonttools brotli
pyftsubset Inter-Regular.ttf \
--unicodes="U+0000-00FF,U+2013-2014,U+2018-201D,U+2026" \
--layout-features="*" \
--flavor=woff2 \
--output-file=Inter-Regular-latin.woff2
Check the license first. Some commercial fonts don’t permit conversion or self-hosting.
How should the server send WOFF2?
Older nginx mime.types files have no entry for WOFF2, so check yours for this line inside its types block and add it if it’s missing. Don’t put a separate types block in your server config; it replaces the whole inherited list.
# in /etc/nginx/mime.types
font/woff2 woff2;
location ~* \.woff2$ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
Two notes. Don’t add gzip or Brotli compression on top: WOFF2 is already Brotli-compressed, so a second pass costs CPU and saves next to nothing. And if fonts are served from a different origin (a CDN hostname), that origin must send Access-Control-Allow-Origin, or the browser refuses to use the font.
What does bseoa check?
bseoa looks at the fonts a page preloads (<link rel="preload" as="font">). If a preloaded font’s URL doesn’t end in .woff2, it reports font not in WOFF2 and font format; query strings like font.woff2?v=3 are handled correctly. It also flags a preload without crossorigin. It doesn’t open your CSS files to read @font-face rules, so fonts that are only referenced from CSS aren’t checked; open DevTools, filter the Network panel to Font, and look at the Type column for those.
To run those checks across every page of your site, the bseoa free trial runs for 14 days with every feature.
Quick answers
Is WOFF2 supported everywhere? In every current browser, yes. Internet Explorer never supported it.
Is WOFF2 lossy? No. Decompressing gives back a font that renders identically.
Should I still ship TTF or EOT? No. EOT was for old Internet Explorer and TTF is uncompressed. WOFF2, with WOFF only if you must support IE11.
What about variable fonts? They work in WOFF2, and one variable WOFF2 file often replaces four or five static weights.
-Sethers