How to shrink web fonts radically without losing a single letter
Font files are often among the largest individual resources on a page, even though most of their content never gets displayed. Font subsetting and variable fonts can drastically cut the transferred data volume without users noticing any visible difference in typography.
Inhaltsverzeichnis
- 1. What Is Font Subsetting
- 2. Why Font Files Get So Large
- 3. Subsetting Tools in the Build Process
- 4. Variable Fonts Basics
- 5. Variable Fonts vs. Static Font Files
- 6. Combining with font-display and a Loading Strategy
- 7. Unicode-Range Splitting for Multilingual Sites
- 8. Automated Subsetting in the CI Pipeline
- 9. Summary and Outlook
- 10. Zusammenfassung
- 11. FAQ
1. What Is Font Subsetting
A typical font file contains thousands of glyphs so it can cover as many languages, special characters, ligatures, and symbols as possible. For an English-language website using the Latin alphabet, only a few hundred of those characters are actually needed in practice, and the rest sits as unused dead weight in the transferred data. Font subsetting is the process of extracting exactly the glyphs a page actually uses from a full font file and discarding everything else.
The result is a new, significantly smaller font file with the exact same visual appearance for the character set that's actually used. A complete weight with Latin, Cyrillic, and Greek glyphs plus extensive OpenType features can easily reach several megabytes, while a subset reduced to English-language needs is often only twenty to forty kilobytes. That difference directly affects load time, especially when the font blocks the rendering of visible text.
2. Why Font Files Get So Large
Modern font families, as offered by Google Fonts or commercial foundries, are usually built for international use and cover dozens of Unicode blocks, from basic Latin through Cyrillic and Greek characters to symbols for currencies, math, and arrows. Every single glyph is stored as a vector outline made of Bezier curves, and the more glyphs a file contains, the larger its central glyph table grows within the font format.
On top of that, OpenType features such as ligatures, alternate character forms, small caps, or fractions require additional lookup tables in the file, even if a website never activates those features. The compressed WOFF2 format reduces file size significantly compared to uncompressed formats, but it can't fully offset the fundamental redundancy of a huge character inventory, because many glyphs are structurally too different from each other to compress efficiently.
3. Subsetting Tools in the Build Process
The reference tool for subsetting is pyftsubset from the Python fonttools package, which reduces a font file based on an explicit character list or a Unicode range while consistently adjusting all relevant font tables. On top of that, the tool glyphhanger automates this step by scanning a project's HTML, CSS, and JavaScript files for characters that are actually used and generating the matching character list for pyftsubset automatically.
In a build pipeline this step can be fully automated, so an up-to-date, minimal subset is generated on every deployment instead of relying on a manually maintained character list that risks going stale. The example below shows a typical invocation that reduces a font file to the basic Latin character set while explicitly keeping the kerning-related layout feature and producing WOFF2 output.
# Subsetting a variable font file down to the basic Latin character set
pyftsubset Inter-Variable.ttf \
--unicodes="U+0020-007E,U+00A0-00FF,U+2018-201E" \
--layout-features="kern,liga,calt" \
--flavor=woff2 \
--output-file=Inter-Variable.subset.woff2
# Automatic character detection straight from the project
glyphhanger ./dist/*.html \
--subset=./fonts/Inter-Variable.ttf \
--formats=woff2 \
--whitelist=U+20AC
4. Variable Fonts Basics
A variable font no longer stores just a single fixed weight, it stores an entire range of styles along defined axes inside one file. The most common axis is wght for font weight, which can be interpolated continuously between a thin and a bold style, and it's often joined by wdth for width, slnt for slant, and opsz for optical size adjustment at different font sizes.
Technically, this relies on interpolation between so-called master outlines that define the extremes of each axis, while intermediate values are computed mathematically. The CSS property font-variation-settings, or the more established shorthand like font-weight: 375, lets you target any point on these axes directly from the stylesheet without ever loading an additional font file.
5. Variable Fonts vs. Static Font Files
A classic typography setup with Regular, Bold, Italic, Bold Italic, Light, and Medium needs six separate font files and therefore six separate HTTP requests, even though HTTP/2 multiplexing no longer forces those requests to happen sequentially. Every one of those files still has to be requested, transferred, and parsed by the browser, which causes noticeable delay on slow mobile connections before text can render in its final weight.
A single variable font file is usually larger than one individual static weight, but taken as a whole it's often smaller than all six static files combined, since shared glyph data only needs to be stored once. The browser cache also benefits from having to hold only one file for any number of weights, which is a clear advantage on sites with many distinct text weights across headings, body copy, and emphasis.
6. Combining with font-display and a Loading Strategy
Subsetting and variable fonts only reach their full potential together with a well-thought-out loading strategy. The CSS property font-display: swap makes the browser show a fallback font first and swap it once the web font has loaded, so text is visible immediately, while font-display: optional can skip the swap entirely if the connection turns out to be too slow.
A <link rel="preload"> with the correct as="font" attribute and matching type value additionally tells the browser to request the small subset file as early as possible, before the CSS has even been fully parsed. Combined with a subset reduced to a few kilobytes, the time to final text rendering is often barely longer than with a plain system font.
7. Unicode-Range Splitting for Multilingual Sites
For websites with multiple language versions, a finer-grained strategy than a single global subset makes sense: the CSS descriptor unicode-range lets you define several @font-face rules for the same logical font family, with each rule responsible for only a specific range of characters. The browser then loads only the font file whose range actually appears in the rendered text and ignores every other rule entirely.
A visitor reading the English version of the site only downloads the Latin subset, while a visitor on a Cyrillic language version automatically pulls in the separate Cyrillic font file, without any explicit language detection needed in the code. This mechanism works purely declaratively through the browser's CSS engine and saves substantial data on international projects compared to a single file covering every language at once.
8. Automated Subsetting in the CI Pipeline
In practice it pays off to add a build step that automatically scans all templates, rendered pages, and CMS content for the characters they contain and generates an up-to-date subset from that on every deployment. The resulting file gets a content hash in its filename, so any change to the character set automatically produces a new URL and the browser cache gets invalidated correctly, without anyone having to think about version numbers manually.
A common pitfall here is dynamic content such as CMS free text fields or user-generated comments that may contain special characters that weren't known at build time. For those cases it makes sense to either deliberately keep a more generous base subset that includes common punctuation like quotation marks, dashes, and currency symbols, or fall back to a more complete font file only for the affected dynamic regions of the page.
9. Summary and Outlook
Font subsetting and variable fonts solve two different but complementary problems: subsetting removes unused character weight, while variable fonts collapse the number of files needed for different weights down to one. Combined with a clean loading strategy using font-display and preload, web font load time can often be reduced by more than eighty percent compared to a naive setup.
Looking ahead, incremental font transfer, also known as progressive font enrichment, is emerging as an approach where the browser loads individual glyphs on demand, similar to progressive image formats. Until that technique reaches broad browser support, manual or automated subsetting combined with variable fonts remains the most reliable way to balance typography and load time.
| Approach | Typical File Size | HTTP Requests | Recommended Use |
|---|---|---|---|
| Full font family | 2 to 5 MB | 1 per weight (6+) | Not recommended for the web |
| Static subset (Latin) | 20 to 40 KB per weight | 1 per weight | Single weight, few styles needed |
| Variable font (unsubsetted) | 150 to 400 KB | 1 total | Many weights, international site |
| Variable font + subset | 40 to 90 KB | 1 total | Best practice for most projects |
| Unicode-range split | 20 to 40 KB per language | 1 per language used | Multilingual websites |
Mironsoft
Web performance, Core Web Vitals, and load time optimization
Load times that don't make users bounce before the page is even visible?
We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.
Performance Audit
Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.
Bundle Optimization
Specifically reducing JavaScript and CSS bundle size and improving code splitting.
Monitoring Setup
Establishing continuous performance monitoring instead of a one-time snapshot.
10. Zusammenfassung
Font Subsetting and Variable Fonts
Technique
Reduce to used glyphs plus one file for all weights
Effect
Often over eighty percent smaller font files
Tooling
fonttools/pyftsubset and glyphhanger in the build process
Risk
Forgotten special characters in dynamic content