ArticlesSoftwareAbout

Self-hosting a web font

TLDR;

Using the woff2 format and font subsetting (eg. via glyphhanger) you can get small font files for hosting web fonts yourself.

Motivation

Lately I’ve found a beautiful mono-spaced font called Iosevka (I like the etoile variant best). For coding I still prefer Cascadia Code but for normal reading I do refer it over cascadia code.

If you actually download the font, you’ll find that the release zip file is ~120MB (or in the ballpark, depending on version), a single ttf file is around ~10MB and even the woff2 version (which is a format already compressed and optimized for usage on the web) is ~2MB in size.

As a custom font is hardly a necessary feature of any webpage and at best a nice-to-have, 2MB is waaay to big for a vanity download.

Considering that in my use case I would probably only need ~60+ chars (10 digits + 26 letters + 26 more in uppercase and a couple of special characters) I wanted a smaller file that only contains the characters that I am using.

Font Subsetting

Turns out that the process of creating a smaller font from a subset of codepoints is called font subsetting (duh!) and there are a couple of nice tools like glyphhanger, which can do that for you if you give them a website to crawl or a list of the characters required.

Putting it together

So I threw together a short Dockerfile that sets up glyphhanger with woff2 export support and a script to instruct it to generate a woff2 subset with the whitelisted characters here.

Dockerfile

The docker file installs glyphhanger via bun and the prerequisites for the enabling the woff2 export:

FROM oven/bun:alpine
RUN apk add python3 py3-pip py3-fonttools py3-brotli py3-zopfli
RUN bun i -g glyphhanger
ENTRYPOINT [ "glyphhanger" ]

Script

The following script volume maps the current directory (which should contain the input ttf files) into the container and instructrs glyphhanger to create a font subset in woff2 format:

docker run -v $(pwd):/home/bun/app glyphhanger --whitelist="/.- 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" --subset=*.ttf --formats=woff2

Running that with my favourite font files, I get the following output files that are ~69KB in size:

-rw-r--r--   1  9.6M Iosevka-Regular.ttf
-rw-r--r--   1   68K Iosevka-Regular-subset.woff2
-rw-r--r--   1  9.6M IosevkaEtoile-Regular.ttf
-rw-r--r--   1   69K IosevkaEtoile-Regular-subset.woff2

Which I find adequate in size for some eye candy.

References

Copyright Martin Kramer 2026
Impressum / About