Patu

Web fonts: WOFF2 is the easy 64%, subsetting is the other 30

Fonts are render-blocking and they are big. A single weight can outweigh all the images above the fold. Two changes cut a typical web font to a fraction of what a raw TTF ships — and most sites do neither.

WOFF2: the free 64%

WOFF2 is a font wrapper that compresses the glyph tables with Brotli and a transform tuned for font data. It is not a different typeface — it is the same font, smaller, and it is supported by every browser in use.

We converted 40 real fonts (Google Fonts TTFs) to WOFF2:

TTF → WOFF2 Reduction
median −64%
range −41% to −83%
40 fonts in aggregate 22 MB → 7 MB

That is the single highest-leverage change you can make to a web font, and it costs nothing: same glyphs, same rendering, one build step. If you serve .ttf or even .woff, you are shipping two to three times the bytes for no reason.

Subsetting: the other 30

Here is the part almost nobody does. Open a Google Font’s TTF and it usually carries far more than Latin — Cyrillic, Greek, Vietnamese, extended punctuation, symbols. An English-language site downloads all of it and renders none of it.

Subsetting drops the glyphs a page will never use. We subset the same 40 fonts to a Latin range (0-2AF plus common web punctuation and the euro) with hb-subset:

TTF → WOFF2, Latin subset Reduction
median −81%
40 fonts in aggregate 22 MB → 1 MB

Read that last row again: 22 MB of TTF becomes about 1 MB once you both convert to WOFF2 and keep only the glyphs a Latin page uses — a 95% cut. WOFF2 alone got 22 MB to 7 MB; subsetting took 7 MB to 1 MB. The two stack, and the second is where the surprise is.

How to subset without breaking things

Subsetting is lossy in the only way that matters: the glyphs you drop are gone. Do it deliberately.

What Patu does with a font

Send Patu a TTF, OTF or WOFF and it returns WOFF2. Pass the text or the unicode ranges the page needs and it subsets to them in the same step — the 95% path, not just the 64% one.

# convert + subset to the glyphs this page actually uses
curl -X POST "https://patu.dev/v1/compress?subset-text=$(cat page.txt)" \
  -H "X-Api-Key: $PATU_KEY" -H "Content-Type: font/ttf" \
  --data-binary @Inter.ttf -o Inter.woff2

The WOFF2 conversion is the change everyone should already have made. The subsetting is the one that turns a font from a real page-weight line item into a rounding error.


Method and data: 40 real fonts (Google Fonts TTFs); WOFF2 via woffify, subsetting via hb-subset to 0-2AF,20AC,2018-201F,2010-2014. Reductions are the median against the source TTF; the aggregate is the summed bytes of all 40.