Why Font File Size Matters
Every font weight is a separate HTTP request. A typical family (regular, medium, bold, italic) is 2–3 MB uncompressed. On slow networks
- FOIT (Flash of Invisible Text): Text stays invisible until fonts load.
- FOUT (Flash of Unstyled Text): System font shows first, then swaps. Creates jarring layout shift.
- Cumulative Layout Shift: Text reflows when font loads, breaking scroll position.
Subsetting and compression reduce load time from 2–3s to 200–400ms, making text instantly readable.
Subsetting Fonts
Subsetting removes unused glyphs. A Latin-only site doesn't need Cyrillic, Greek, or CJK characters. Subsetting a font to Latin + punctuation cuts file size by 60–80%.
### Using Glyphhanger
```bash # Install glyphhanger (requires Node.js) npm install -g glyphhanger
# Subset a font to Latin only glyphhanger --LATIN --subset=font.ttf
# Subset to specific text (for headings) glyphhanger --whitelist="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" --subset=heading.ttf ```
### Using Fonttools (Python)
```bash # Install fonttools pip install fonttools brotli
# Subset to Latin pyftsubset font.ttf \ --output-file=font-subset.woff2 \ --flavor=woff2 \ --layout-features='*' \ --unicodes=U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD ```
This reduces a 200 KB TTF to 40–60 KB WOFF2.
Variable Fonts vs. Static Families
Variable fonts store multiple weights in one file. Instead of 6 files (Thin, Light, Regular, Medium, Bold, Black), one variable font covers 100–900 weight range.
Pros
- Smaller total file size (one 80 KB file vs. six 40 KB files = 240 KB saved).
- Smooth weight transitions (animate from 400 to 700).
- Fewer HTTP requests.
Cons
- Not all fonts offer variable versions.
- Older browsers (IE11) don't support variable fonts (fallback to static).
Use variable fonts when you need 3+ weights. For one or two weights, static fonts are smaller.
Format Conversion: WOFF2 is King
Font formats by size (same typeface)
- TTF/OTF: 200 KB (uncompressed)
- WOFF: 120 KB (compressed)
- WOFF2: 80 KB (Brotli compression, ~40% smaller than WOFF)
Modern browsers (Chrome, Firefox, Safari, Edge) support WOFF2. Skip WOFF and TTF unless supporting IE11.
### Converting to WOFF2
```bash # Using fonttools pyftsubset font.ttf --output-file=font.woff2 --flavor=woff2 ```
Or use online tools: everythingfonts.com, transfonter.org.
Licensing and Legal Compliance
Before subsetting or self-hosting
- Check the license: Read the font's EULA. Some licenses prohibit modification or self-hosting.
- Google Fonts: Free to subset, modify, and self-host (SIL Open Font License).
- Commercial fonts: Adobe Fonts, MyFonts, Monotype often prohibit subsetting. Check before optimizing.
- Embedding permissions: Some fonts allow web embedding but not subsetting. Verify OTF/TTF embedding bits.
If unsure, email the foundry. Licensing violations risk legal action.
Google Fonts Best Practices
Google Fonts are pre-optimized but can be further tuned
### 1. Limit Weights and Styles
```html
<!-- Bad: loads 12 font files -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Good: loads 2 files -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> ```
### 2. Self-Host for Performance
Download Google Fonts and serve from your CDN. Eliminates DNS lookup and TLS handshake to fonts.googleapis.com.
```bash # Download Google Font curl -o inter.woff2 "https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2" ```
Then reference in CSS
```css @font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
} ```
### 3. Use `font-display: swap`
Prevents FOIT. Text shows immediately in fallback font, then swaps when custom font loads.
```css @font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* Critical */
} ```
Testing and Validation
Before deploying
- File size check: Optimized fonts should be 40–80 KB per weight. If larger, subset more aggressively.
- Render test: Open the font in FontDrop (fontdrop.info) to verify glyphs render correctly after subsetting.
- Cross-browser test: Test in Chrome, Firefox, Safari. Ensure fallback fonts work in IE11 if supporting it.
- Lighthouse audit: Run Google Lighthouse. Fonts should load in <500ms on 3G.
Subsetting, WOFF2 conversion, and self-hosting reduce font load times from seconds to milliseconds, making text instantly readable.