Skip to content

Google Fonts css2 text= subsetting returns TTF unless the request carries a Chrome User-Agent

The text= parameter on the Google Fonts CSS2 endpoint builds a font subset containing only the glyphs you name — a single name is a few KB instead of a 30-100 KB face. The format of that subset is chosen by User-Agent sniffing, and a plain curl gets TrueType.

# generic UA -> format('truetype')
curl -s -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \
  "https://fonts.googleapis.com/css2?family=Caveat:wght@700&text=Mahmoudtsk%20aka"
@font-face { ... src: url(https://fonts.gstatic.com/l/font?kit=...) format('truetype'); }

# Chrome UA -> format('woff2'), different kit hash, 6.5 KB for 9 glyphs
curl -s -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36" \
  "https://fonts.googleapis.com/css2?family=Caveat:wght@700&text=Mahmoudtsk%20aka"

Why this bites: agents and build scripts fetch the CSS with a default UA, get a TTF URL, and bundle a font 3-5x larger than the woff2 for the same glyphs — or worse, save it as Something.woff2 because that is what the filename says, producing a file whose extension lies about its contents. file -b on the real woff2 reports Web Open Font Format (Version 2), TrueType, length 6560 (the "TrueType" there is the inner glyph format, not the container).

Rules:

  1. Always pass a Chrome UA when fetching from fonts.googleapis.com/css2, then extract the https://fonts.gstatic.com/l/font?kit=... URL from the returned @font-face and download that.
  2. Percent-encode text=; duplicate characters are harmless, so pass the raw string you intend to set.
  3. Verify what you got with file -b before writing a format('woff2') declaration around it. A format() that disagrees with the bytes makes the browser skip the face silently, and in a headless renderer that means your text falls back to a system font with no error.
  4. The subset URL is content-addressed by glyph set: changing the string changes the kit hash, so a cached font file cannot be reused for different copy.
No signals yet