← Back to Blog

You're shipping 200KB of font glyphs your visitors will never see

You're shipping 200KB of font glyphs your visitors will never see

A single weight of Inter in WOFF2 format is about 96KB. That covers Latin, Latin Extended, Cyrillic, Cyrillic Extended, Greek, Greek Extended, and Vietnamese character sets. If your site is English-only, you need the Latin range: roughly 230 glyphs out of 2,548 in the full file. That's 9% of the glyphs doing 100% of the work, and the other 91% ships to every visitor on every page load.

Multiply that by the number of weights you use (regular, bold, italic, bold italic is common), and you're looking at 300-400KB of font data where 30-40KB would cover everything your pages actually display.

Why this matters for performance

Font files are render-blocking by default. The browser won't paint text until the font loads, which is why you see the flash of invisible text (FOIT) or flash of unstyled text (FOUT) on slower connections. A 96KB font file over a 3G connection takes roughly 800ms to download. A subsetted 12KB version of the same font takes about 100ms. That's 700ms of text visibility you're leaving on the table.

font-display: swap helps with the perceived delay by showing fallback text immediately, but the layout shift when the web font loads still counts against your Cumulative Layout Shift score. Smaller font files load faster, which means less time in the fallback state and smaller layout shifts when the swap happens.

Google Fonts actually does subsetting automatically through its unicode-range CSS declarations. When you use the Google Fonts API, it splits each font into subsets by character range and only downloads the subsets your page needs. But if you self-host your fonts (which you should, for privacy and performance reasons), you're responsible for subsetting yourself.

What subsetting actually means

Subsetting is removing glyphs from a font file that your site won't use. The most basic level strips everything outside the Latin character range for an English site. A more aggressive level strips even Latin characters you'll never use (like the Icelandic eth character or the Polish ogonek).

The tool chain is straightforward:

pyftsubset from the fonttools Python package is the standard. Give it a font file and a Unicode range, and it outputs a new font file with only those glyphs:

pyftsubset MyFont-Regular.ttf \
  --output-file=MyFont-Regular-subset.woff2 \
  --flavor=woff2 \
  --unicodes="U+0000-007F,U+2000-206F,U+2074,U+20AC,U+2122,U+2190-21BB,U+2212,U+2215,U+FEFF,U+FFFD"

That Unicode range covers Basic Latin, General Punctuation, the Euro sign, the trademark symbol, and common arrows. It's enough for 99% of English-language web content.

glyphhanger from Filament Group takes it further. Point it at a URL, and it crawls your pages to find exactly which Unicode code points appear in your content, then generates the minimal subset. This is the most aggressive approach and produces the smallest files, but you need to re-run it if your content changes significantly.

What the Font Subsetting Audit checks

The Font Subsetting Audit analyzes the fonts loaded by your page and reports:

File sizes for each font resource, with estimates of how much could be saved by subsetting to common ranges (Latin, Latin Extended, or content-specific).

Unicode range coverage. If your CSS declares unicode-range in @font-face rules, the audit checks whether those ranges match the characters actually used on the page. Overly broad ranges mean you're downloading glyphs you don't need. Missing ranges mean some characters fall back to system fonts unexpectedly.

Multiple weight detection. Sites loading 4+ weights of the same family often don't need all of them. The audit flags this because variable fonts (a single file that covers all weights) may be a better option for sites using three or more weights.

WOFF2 format check. Fonts served as TTF, OTF, or even WOFF (version 1) are significantly larger than WOFF2. The audit flags any font not in WOFF2 format as a quick win.

Self-hosting vs. Google Fonts

If you use Google Fonts, you get automatic subsetting but you also send your visitor's IP address and user agent to Google on every page load. Courts in Germany and Austria have ruled this violates GDPR without explicit consent. Self-hosting eliminates the privacy concern and gives you full control over subsetting, caching, and preloading.

The workflow: download the font files from the Google Fonts repository (or use google-webfonts-helper), subset them with pyftsubset, convert to WOFF2, and serve them from your own domain with aggressive cache headers. Total setup time for a typical two-weight font family is about 15 minutes.

Run the Font Subsetting Audit alongside the Font Loading Strategy Audit to optimize how your subsetted fonts load, and the Remote Dependency Audit to identify all external font sources that could be self-hosted.

For building sites that run fast from the start, The $97 Launch includes a font-minimal approach that keeps total font payload under 50KB.

Fact-check notes and sources

Related reading

This post is informational, not web-performance consulting advice. Mentions of Google, Inter, and other projects are nominative fair use. No affiliation is implied.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026