Font Subsetting and Variable Fonts for Better Performance
AI generated
60fps
ms
Web Performance / Fonts
Font Subsetting and Variable Fonts for Better Performance
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.

14 min read Font Subsetting Variable Fonts

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

11. FAQ: Font Subsetting and Variable Fonts

1What's the difference between font subsetting and variable fonts?
Subsetting removes unused glyphs from a font file to make it smaller. Variable fonts bundle multiple weights like Regular and Bold into a single file with interpolatable axes. Both techniques can be combined by subsetting a variable font as well.
2How much can subsetting typically save?
For a font family reduced to English-language needs, savings of eighty to ninety percent compared to the full international font file are common. A file of several megabytes often shrinks down to just a few tens of kilobytes.
3Which tool is best for automated subsetting?
The Python package fonttools with its command-line tool pyftsubset is the de facto standard for manual and scripted subsetting. For automatically detecting used characters from HTML files, glyphhanger is a useful complement.
4Do variable fonts work in all modern browsers?
All current versions of Chrome, Firefox, Safari, and Edge fully support variable fonts. For very old browser versions a fallback to static weights via feature detection can make sense, though it's rarely necessary with modern traffic.
5What happens if a character is missing from the subset?
If a character is missing from the subset, the browser falls back to the next font defined in the font-family stack, or shows a placeholder glyph like a box for that spot. That's why the subset should be generous enough to cover every realistically occurring special character.
6How does unicode-range help with multilingual websites?
The CSS descriptor unicode-range lets you define several font files for the same family covering different character ranges. The browser automatically loads only the file whose range appears in the actually rendered text, without needing explicit language switching in code.
7Should I use font-display: swap or optional?
Swap shows a fallback font immediately and swaps it later, causing a brief style change but always making text visible right away. Optional lets the browser skip the swap entirely on a slow connection, avoiding the style change but using the web font less often.
8Does subsetting need to run on every deployment?
If content and therefore the character set can change, an automated subsetting step on every build is recommended. For static sites with a fixed character set, a one-time manual run that's repeated only when content changes is sufficient.
9Can you subset a variable font's axes, keeping only part of the range?
Yes, pyftsubset lets you restrict the value range of individual axes, for example keeping only weights between 400 and 700 instead of the full range. That reduces file size further when only part of the axis is actually used.
10How do you verify a subset contains every needed character?
A simple test is rendering every page template with the new subset in a staging environment and visually checking for missing glyphs. Automated comparisons between the character set contained in the subset and one extracted from actual page content can also be added to the CI pipeline.