Custom fonts are one of the most common reasons a fast-looking site still fails its Core Web Vitals. A single weight of a typical TrueType font can run 150KB to 400KB, and most teams ship four or five of them. The browser has to download, parse, and apply every one before your headlines stop shifting around. The good news: with three steps you can usually cut font payload by 50 to 70 percent without touching a single design decision.
Here is the exact workflow I use on production sites, with a real before-and-after at the end.
Why your .ttf file is heavier than it needs to be
A raw TTF carries two kinds of weight you almost never need on the web:
● The full glyph set. A professional font can include 2,000+ glyphs covering Latin, Cyrillic, Greek, math symbols and more. An English-only marketing site renders maybe 200 of them.
● No web compression. TTF and OTF are desktop formats. They are not pre-compressed for transport, so you are pushing raw outline data over the wire.
WOFF2 fixes the second problem (it is Brotli-compressed and built for the web), and subsetting fixes the first. Do both and the savings stack.
Step 1: Subset to the glyphs you actually use
Before you convert anything, strip the glyphs you will never render. This is the single biggest win and the one most people skip.
Decide which character ranges your site needs. For most English-language sites that is Basic Latin plus a handful of punctuation and currency symbols. If you support European languages, add Latin Extended. Then run the file through a font subsetter and keep only those ranges.
A 400KB font carrying a full multi-script glyph set routinely drops to 60 to 90KB once it is subset to Basic Latin, before any format conversion. If you self-host for multiple languages, subset per language and load each file with its own unicode-range so the browser only downloads what the page actually needs.
Step 2: Convert the subset font to WOFF2
Now take the slimmed-down file and convert TTF to WOFF2. WOFF2 is supported by every browser you care about in 2026 and gives roughly 30 percent better compression than the older WOFF format.
If you are juggling several files (different weights and styles), a reliable font converter will batch them in one pass so you are not processing Regular, Medium and Bold one at a time. The output is a set of .woff2 files that are a fraction of the original size and ready to drop straight into your CSS.
You generally do not need WOFF v1 as a fallback anymore. WOFF2 coverage is effectively universal, and shipping both just doubles your requests.
Step 3 (optional): Inline a critical font with Base64
For the one font that renders your above-the-fold headline, you can remove a network request entirely by embedding it directly in your CSS as a data URI. Encode the .woff2 to a Base64 string and paste it straight into your @font-face src.
This is a trade-off, not a free win. Inlining stops the request from happening at all, which helps Largest Contentful Paint for that one face, but the font is no longer cached separately and it bloats your CSS file. Reserve it for a single critical weight. Everything else should stay an external, cacheable .woff2.
Wiring it up with @font-face
Once you have subset, compressed files, the CSS is straightforward. The piece people forget is font-display: swap, which tells the browser to render fallback text immediately instead of hiding your content while the font loads:
@font-face {
font-family: “Brand Sans”;
src: url(“/fonts/brand-sans.woff2”) format(“woff2”);
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: “Brand Sans”;
src: url(“/fonts/brand-sans-bold.woff2”) format(“woff2”);
font-weight: 700;
font-display: swap;
}
Then preload the single most important face so the browser fetches it early:
<link rel=”preload” href=”/fonts/brand-sans.woff2″
as=”font” type=”font/woff2″ crossorigin>
When you need to go the other way
Sometimes you receive a web font and need it back on the desktop, for example to open it in a design tool, inspect its metrics, or hand it to a print workflow. Those tools do not read WOFF2. In that case convert the file back to an editable TrueType (.ttf) file first. It is the same workflow in reverse, and worth keeping in mind for the day a client sends you nothing but the compiled web assets.
The before-and-after
Here is a real result from a recent marketing site that shipped four weights of a single TrueType family:
| Stage | Total font payload | Notes |
| Original 4x .ttf | 1.46 MB | Full glyph set, uncompressed |
| After subsetting | 412 KB | Basic Latin + punctuation only |
| After WOFF2 | 196 KB | Brotli compression on the subset |
| Net change | -87% | No visible difference to the design |
Font payload went from 1.46MB to under 200KB, and the Largest Contentful Paint improvement was enough to move the page from “Needs Improvement” into the green. Verify your own numbers with Lighthouse or WebPageTest before and after, so you can show the win rather than assume it.
A five-minute checklist
- List the character ranges your site actually renders.
- Subset every weight to those ranges.
- Convert each subset file to WOFF2.
- Inline only your one critical above-the-fold face as Base64.
- Set font-display: swap and preload the primary face.
- Drop WOFF v1 and any unused weights entirely.
None of this touches your typography or your brand. It just stops you from shipping megabytes of glyphs and outline data that no visitor will ever see. Run the steps once, bake them into your build, and web fonts stop being the thing that quietly drags your Core Web Vitals down.
