A typical Google Fonts file contains glyphs for Latin, Cyrillic, Greek, and often additional writing systems. A German-language website only needs 10–15% of those. Font subsetting removes all unnecessary characters and drastically reduces file size – a typical variable font subset comes in at 20–40 KB instead of 100+ KB.

What Font Subsetting Is

Font subsetting describes the process of extracting only the actually needed glyphs from a complete font file. A typeface like Inter contains over 2,500 glyphs covering Latin, Cyrillic, Greek, Vietnamese, and many more writing systems. A German-language website typically needs 250–350 characters: the basic Latin alphabet, German umlauts (ä, ö, ü, ß), punctuation, digits, and some special characters.

The result: a font file that weighs 25–40 KB instead of 150 KB. With four to six font weights, the savings add up to several hundred kilobytes – a measurable performance gain, especially on mobile connections.

Unicode Ranges and Character Sets

The key to effective subsetting is understanding Unicode ranges. The most relevant for European websites:

Basic Latin (U+0020–U+007F) includes 95 printable characters: the English alphabet, digits, punctuation, and standard symbols. The absolute minimum for any Western European website.

Latin-1 Supplement (U+0080–U+00FF) adds European special characters: German umlauts (ä, ö, ü), French accents (é, è, ê, ë), Spanish special characters (ñ, ¿, ¡), and more. Essential for multilingual EU websites.

Latin Extended-A (U+0100–U+017F) contains characters for Eastern European languages: Czech (č, ř, ž), Polish (ł, ń, ź), Hungarian (ő, ű), Romanian (ș, ț). Relevant for organizations targeting Eastern European audiences.

For a typical DACH website (Germany, Austria, Switzerland), Basic Latin + Latin-1 Supplement suffices. For an EU-wide website, including Latin Extended-A is recommended.

Tools and Methods

pyftsubset (fonttools)

The Python-based pyftsubset is the most powerful command-line tool for font subsetting:

pyftsubset InterVariable.woff2 
  --output-file=inter-subset.woff2 
  --flavor=woff2 
  --layout-features='*' 
  --unicodes="U+0020-007F,U+00A0-00FF,U+0100-017F,U+2000-206F"

The --layout-features='*' option preserves all OpenType features (ligatures, kerning, tabular figures). This is important because a subset without OpenType features would be typographically damaged.

Glyphhanger

glyphhanger (by Zach Leatherman) analyzes a website or HTML file and automatically generates the minimal Unicode range:

npx glyphhanger https://example.com --subset=InterVariable.woff2

The tool crawls the specified URL, identifies all used characters, and creates an optimal subset.

Fontsource with Automatic Subsetting

Fontsource provides fonts in pre-built subsets. Only the needed subset is loaded during installation:

import '@fontsource-variable/inter/latin.css';    // Latin only
import '@fontsource-variable/inter/latin-ext.css'; // Latin + Extended

CSS unicode-range: Intelligent Loading

The CSS property unicode-range tells the browser which characters a font file contains. The browser only downloads the file if characters from that range actually appear on the page:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0020-007F, U+00A0-00FF;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-017F;
  font-display: swap;
}

In this example, inter-latin-ext.woff2 is only loaded if the page actually contains characters from the Extended Latin range. For multilingual websites, this is an elegant way to keep initial load time minimal for the primary language while loading characters for additional languages on demand.

Subsetting in the Build Pipeline

For design systems, font subsetting should be integrated into the build pipeline, not performed manually. Vite plugins, PostCSS plugins, or CI/CD build steps can automate the process, ensuring subsets are regenerated whenever the font or language coverage is updated.

Risks and Pitfalls

Missing characters are the biggest risk in subsetting. Overly aggressive subsetting can omit quotation marks, em dashes, ellipses, or currency symbols. The browser then falls back to a system font, causing visible inconsistencies.

OpenType features can be damaged by subsetting. Kerning pairs referencing characters not in the subset are removed. Contextual ligatures may break. The solution: preserve --layout-features='*' in pyftsubset and visually verify after subsetting.

Dynamic content such as user-generated text, CMS content in various languages, or internationalized UI text requires broader subsets than static websites. Here, unicode-range with multiple font files offers the most flexible solution.

Comparison of Subsetting Strategies

Strategy
File size
Coverage
Complexity
Risk
No subsetting
100–150 KB
Complete
None
None
Fontsource latin
30–40 KB
Western EU
Low
Low
Custom pyftsubset
20–35 KB
Exactly matched
Medium
Missing characters
Glyphhanger (crawl)
15–25 KB
Minimal
High
Dynamic content
unicode-range split
25–50 KB (initial)
Progressive
Medium
Browser support

Key Takeaways

  • Font subsetting removes unneeded glyphs and typically reduces file sizes by 60–80%.
  • For DACH websites, Basic Latin + Latin-1 Supplement suffices; EU-wide, Latin Extended-A is recommended.
  • CSS unicode-range enables progressive loading: primary language immediately, additional languages on demand.
  • pyftsubset and Glyphhanger are the key tools – subsetting should be integrated into the build pipeline.
  • OpenType features must be preserved during subsetting, otherwise typographic quality suffers.
  • Dynamic content and multilingual websites require broader subsets than static pages.
  • Combined with variable fonts and self-hosting, font subsetting yields the optimal font infrastructure.