Font Subsetting: The Complete Guide to Optimizing Your Web Fonts

A glowing digital hand carving a dark stone
block filled with jumbled letters to extract an optimized font

In the quest for an ultra-fast website, every millisecond counts. Custom fonts bring a real visual identity. Yet, they are often one of the most significant factors slowing down page load times.

This publication launches our series of 7 articles dedicated to extreme web font optimization. Throughout this series, we will break down the most advanced techniques to make your fonts weightless on the performance scale.

In this first installment, we tackle the most powerful technique to reduce file sizes: font subsetting (or character set reduction).


Why is a font file so heavy?

When you download a classic font from the Internet (in TTF or OTF format), it is designed to work worldwide. To be universal, it must integrate a massive amount of data:

  • Thousands of glyphs: A glyph is the individual drawing of a character. A complete font often contains Latin, Cyrillic, Greek, Hebrew alphabets, or even Asian ideograms.
  • Complex OpenType features: Ligatures (merging letters like “œ” or “fi”), alternate glyphs, and mathematical styles.
  • Kerning tables: Precise rules to adjust the spacing between specific pairs of letters (like “A” and “V”) to make reading comfortable.
  • Hinting instructions: Embedded code that tells the browser how to render each pixel of the letter on low-resolution screens.

The result? A single font file can easily weigh between 150 KB and 700 KB. If you use multiple weights (light, regular, bold, extra-bold) along with their italic versions, your users could find themselves downloading over 1.5 MB of fonts just to display text. On an unstable mobile network, this is a disaster for your performance.


The principle of Font Subsetting: carving out the essentials

Font subsetting consists of cutting down your original font file to produce a miniature version. The goal is simple: remove all glyphs and metadata that your site will never use.

Imagine a sculptor facing a huge block of stone containing thousands of engraved letters. Subsetting is the digital chisel that will eliminate all unnecessary stone to keep only the few characters you actually need.

For a site written only in French or English, you only need about 100 to 150 different characters. This includes:

  1. Lowercase and uppercase letters of the basic Latin alphabet.
  2. Digits and common punctuation.
  3. Specific accents (é, è, à, ç, û, œ, etc. for French).
  4. A few essential symbols like the Euro symbol (€).

By isolating only these elements, a 250 KB font file can be reduced to just 15 KB. This is a weight reduction of over 90% achieved without any loss of visual quality for the user.


Step 1: Listing your actual needs with Unicode ranges

To cut down our font, we must first define exactly which characters to keep. In computing, each character is identified by a unique code called a Unicode code point.

Here are the essential Unicode ranges for a website written in French and English:

  • U+0020-007F: This is the basic block (ASCII). It contains all letters from “a” to “z” (lowercase and uppercase), digits from 0 to 9, and standard punctuation (periods, commas, question marks).
  • U+00A0-00FF: This is the “Latin-1 Supplement” block. It contains most accents (é, è, à, ç, ù, ô, î, ë, ï) as well as the copyright symbol (©).
  • U+0152-0153: These are the specific code points for the ligature “œ” and its uppercase version “Œ”, which are very common in French.
  • U+20AC: This is the exact code point for the Euro currency symbol (€).

If you want to automate the detection of characters actually displayed on your site, you can use a command-line tool like Glyphhanger. This tool scans your site’s pages, detects all written characters, and instantly provides the exact list of Unicode codes to keep.


Step 2: Creating the subset with FontTools and Python

While visual online tools exist to generate fonts (such as the famous Font Squirrel), they do not always allow for the most advanced optimization or support the modern WOFF2 format.

The most performant and professional method is to use the official FontTools command-line utility, written in Python.

1. Installing the tools

Open your terminal and install the library along with the Brotli compression algorithm (essential for writing highly compressed WOFF2 files):

pip install fonttools brotli

2. Running the subsetting command

Once installed, you will have access to a powerful tool named pyftsubset. Here is the command to generate an exceptionally lightweight font file:

pyftsubset 'MyFont-Regular.ttf' \
  --output-file='MyFont-Subset.woff2' \
  --flavor=woff2 \
  --unicodes='U+0020-007F,U+00A0-00FF,U+0152-0153,U+20AC' \
  --layout-features='kern' \
  --no-hinting \
  --desubroutinize

Detailed explanation of the command options:

  • --flavor=woff2: Forces encoding in WOFF2 format, which offers the best compression rate currently available for the web.
  • --unicodes="...": Specifies the exact characters we want to keep. Everything else is permanently removed.
  • --layout-features='kern': Instructs the tool to discard all complex font features except for kerning. Keeping kerning ensures that the spacing between your letters remains perfect and readable.
  • --no-hinting: Removes rendering instructions for old screens. On modern high-density screens (Retina, recent smartphone displays), these instructions are completely unnecessary and bloat the file.
  • --desubroutinize: Optimizes the internal structure of fonts based on CFF outlines (usually from .OTF files) to further reduce memory footprint.

Step 3: High-Performance CSS Integration

Once your MyFont-Subset.woff2 file is generated, it is time to declare it in your stylesheet. In a context where absolute speed is key, we must use a clean code structure without redundant rules.

Here is how to integrate your new font optimally:

@font-face {
  font-family  : 'Geist-Subset';
  src          : url('/fonts/MyFont-Subset.woff2') format('woff2');
  font-weight  : 400;
  font-style   : normal;
  font-display : swap;
}

body {
  font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.article-title {
  font-family : 'Geist-Subset', sans-serif;
  font-size   : clamp(1.8rem, 4.5vw + 1rem, 3.2rem);
  line-height : 1.25;
}

Using fluid typography prevents visual layout jumps when resizing the screen, while avoiding redundant CSS declarations.


Bandwidth Gains and SEO Impact

Why spend so much effort on carving down font files? Drastically reducing file size brings direct benefits to both your network infrastructure and your search engine indexing:

  • Reduced transfer time: By dropping from 300 KB to 15 KB, the font downloads almost instantly. On an unstable mobile network, this prevents the browser from blocking the rendering of your text while waiting for oversized files to load.
  • Saving system resources: Decompressing and parsing a massive font file strains your users’ devices’ processors (CPU). A lightweight file reduces this workload, which is crucial for overall responsiveness on low-to-mid-range mobile devices.
  • Crawl budget optimization: Search engine bots (like Googlebot) allocate a limited amount of time to crawl each site. By drastically reducing the weight of assets required to render your pages, you allow bots to crawl and index your content much more efficiently.

Conclusion and Series Outlook

Font subsetting is the indispensable foundation of any modern typography optimization strategy. By stripping away unnecessary data at the source, you offer your users an instant and eco-responsible browsing experience.

However, the journey to ultimate optimization does not end here. In our next series installment (Post 2 - system fonts and variable fonts), we will analyze how to leverage fonts already present on your users’ devices and how to optimally configure variable fonts to load a single dynamic file without compromising your performance.


Frequently Asked Questions

Can I use subsetting on commercial fonts? This depends entirely on your font's End User License Agreement (EULA). Some licenses strictly forbid modifying or converting original font files. Always check your usage rights before processing a file with online tools or local scripts.
Does discarding "hinting" degrade the visual rendering? On very old computers running Windows XP with CRT or low-resolution screens, yes, letters may appear slightly blurry. However, on all modern smartphones, tablets, and recent computers, the operating system's rendering engine handles smoothing perfectly without needing hinting instructions. The byte savings make this removal highly justified for the modern web.
What happens if a user types a character missing from the subset? If your page contains a character that was not included in your subset file (for example, a Cyrillic character), the browser will automatically fallback to the default system font defined in your CSS to display only that specific character. This is why it is always recommended to configure clean fallback system fonts.
Are there risks of bugs with kerning after subsetting? By using the pyftsubset tool with the --layout-features="kern" option, critical kerning data from the original font is perfectly preserved for the remaining characters. Your texts will retain their optimal spacing, avoiding any ugly overlapping letters.
Lionel Péramo
Lionel Péramo
Web Performance & Eco-design Expert

Full Stack Developer and creator of the OTRA framework (PHP) and EcoComposer library. I write to make the web faster and more inclusive.

About me →