Font Converter

Font Optimization Checklist: Complete Performance Guide

A comprehensive checklist for optimizing web font performance covering format selection, subsetting, loading strategies, caching, fallbacks, and Core Web Vitals

TL;DR

In Simple Terms

Convert all web fonts to WOFF2 (Inter: 288KB TTF to 98KB WOFF2, a 66% reduction. Roboto: 220KB to 53KB, 76% reduction). Subset to Latin-only for English sites (93% total reduction). Target under 100KB total font payload.Use font-display: swap + preload for critical fonts (only 12% of sites use preload -- massive opportunity). Match fallback font metrics with size-adjust/ascent-override to reduce CLS from 0.05-0.15 to under 0.01. Limit to 2-4 font files per page.Self-host fonts (provides 180ms LCP improvement over Google Fonts CDN) with immutable cache headers (max-age=31536000). Browser cache partitioning since 2020 eliminates the shared cache advantage of CDNs. Variable fonts at 40% adoption save up to 91% when replacing 9 static weight files.

Share this page to:

1. Format Selection

All web fonts are in WOFF2 format (Brotli compression, 30-50% smaller than TTF)
WOFF fallback is provided only if you need to support IE11 or very old browsers
No TTF, OTF, EOT, or SVG fonts are served to web browsers
Font files are generated from the highest-quality source available (OTF preferred over TTF as source)
FormatExample Size (Inter Regular)Savings vs TTF
TTF (uncompressed)288 KBBaseline
WOFF (zlib)195 KB-37%
WOFF2 (Brotli)98 KB-66%
WOFF2 + Latin subset23 KB-93%

2. Subsetting & Character Reduction

Fonts are subsetted to include only the character ranges your site actually uses
Latin-only sites use U+0000-00FF subset (Basic Latin + Latin-1 Supplement)
unicode-range is declared in @font-face so browsers download only needed subsets
Unused OpenType features are removed if not needed (saves 5-15KB per file)
Only the font weights actually used in CSS are included (not the entire family)
# Subset with pyftsubset (from FontTools)
pyftsubset input.woff2 \
  --unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD" \
  --layout-features="kern,liga,clig,calt" \
  --flavor=woff2 \
  --output-file=output-latin.woff2

# Result: 310KB full font → 23KB Latin WOFF2 subset

For detailed subsetting instructions, see our Font Subsetting guide and language-specific guides for Chinese, Japanese, and Arabic fonts.

3. Loading Strategy

font-display is set on every @font-face rule (never rely on browser defaults)
font-display: swap for body/heading fonts (show fallback instantly, swap when ready)
font-display: optional for non-critical fonts (use if loaded fast, skip if not)
Critical above-the-fold fonts are preloaded: <link rel='preload' as='font' crossorigin>
Maximum 2 fonts preloaded (preloading more hurts other resource loading)
No more than 4 font files loaded per page (Regular, Bold, and their italic variants max)
Font CSS is inlined or loaded in <head> (not async/deferred)

Pro Tip

For advanced loading patterns like two-stage rendering and Font Loading API, see our Progressive Font Loading guide. For the fundamentals, start with the Font Loading Strategies guide.

4. Caching & Delivery

Fonts are self-hosted, not from third-party CDNs (self-hosting provides ~180ms LCP improvement; Google Fonts used by 54% of sites but cache partitioning eliminated shared cache benefit)
Cache-Control header is set: public, max-age=31536000, immutable
Font filenames include version or hash for cache busting: brand-v2.3.woff2
CORS headers are set if fonts are served from a different domain/CDN
HTTP/2 or HTTP/3 is enabled for multiplexed font delivery
Fonts are served from the same origin as the page (avoids DNS/TLS overhead)
# Nginx font caching configuration
location ~* \.woff2$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Access-Control-Allow-Origin "*";
    add_header Content-Type "font/woff2";
}

# Vercel / Next.js: next.config.js
module.exports = {
  async headers() {
    return [{
      source: '/fonts/:path*',
      headers: [
        { key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
      ],
    }];
  },
};

5. Fallback Font Matching

Every custom font has a system font fallback in the font-family stack
Fallback font metrics are matched using size-adjust, ascent-override, descent-override
CLS from font swap is measured and below 0.05
Framework font tools are used where available (next/font, Fontaine, Capsize)
/* Metric-matched fallback font */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107.64%;
  ascent-override: 90.49%;
  descent-override: 22.56%;
  line-gap-override: 0%;
}

body {
  font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
}

/* Tools to calculate these values:
   - next/font (automatic for Next.js)
   - Fontaine (github.com/unjs/fontaine)
   - Capsize (seek-oss.github.io/capsize/)
*/

6. Variable Font Optimization

If using 3+ weights of the same font, evaluate switching to the variable font version (39% desktop, 41% mobile adoption and growing)
Variable font axes are limited to only what you use (weight, width, etc.)
Unused axes are removed with fonttools instancer to reduce file size
CSS uses font-variation-settings or font-weight with ranges instead of fixed values
/* Variable font @font-face */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-variable-latin.woff2') format('woff2-variations');
  font-weight: 100 900;  /* Full weight range in one file */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF;
}

/* Use any weight without additional file downloads */
h1 { font-weight: 800; }
body { font-weight: 400; }
.light { font-weight: 300; }

/* Trim unused weight range with fonttools */
/* fonttools instancer InterVariable.ttf wght=400:700 */

For a complete guide, see our Variable Fonts guide.

7. Core Web Vitals Impact

MetricHow Fonts Affect ItOptimization
LCP (Largest Contentful Paint)If LCP element uses custom font, font load time adds to LCPPreload LCP font, use font-display: swap, self-host
CLS (Cumulative Layout Shift)Font swap causes text reflow when metrics differMatch fallback metrics, use font-display: optional
INP (Interaction to Next Paint)Font file parsing can delay frame renderingKeep total font count low, use WOFF2 for fast parsing
FCP (First Contentful Paint)Render-blocking font requests delay FCPAvoid FOIT with font-display, preload critical fonts

Complete Optimization Checklist

Use this consolidated checklist before every major deployment:

All web fonts are WOFF2 format
Fonts are subsetted to required character ranges
Total font payload is under 100KB (max 150KB)
No more than 4 font files loaded per page
font-display is set on every @font-face rule
1-2 critical fonts are preloaded in <head>
Fallback fonts have matched metrics (size-adjust, ascent-override)
Fonts are self-hosted on same origin
Cache headers: public, max-age=31536000, immutable
Font filenames include version/hash for cache busting
unicode-range is declared for multi-subset fonts
Variable fonts considered if 3+ weights are needed
CLS from font loading is under 0.05
LCP is not blocked by font loading
Lighthouse reports no font-related warnings
Font licenses verified for web usage

Next Steps

Run a Font Audit to identify current issues, then work through this checklist systematically. For ongoing monitoring, integrate font performance budgets into your CI/CD pipeline.

Sarah Mitchell

Written & Verified by

Sarah Mitchell

Product Designer, Font Specialist

Font Optimization FAQs

Common questions about optimizing web font performance