CSS Typography

วิธีเพิ่มประสิทธิภาพความเร็วในการโหลดฟอนต์เว็บ

Updated กุมภาพันธ์ 24, 2026
รายการตรวจสอบที่ใช้งานได้จริงสำหรับการทำให้ฟอนต์เว็บโหลดเร็วขึ้น — preloading, subsetting, font-display และแนวทางปฏิบัติที่ดีที่สุดในปัจจุบันที่ได้ผลจริง

How to Optimize Web Font Loading Speed

Web fonts are one of the most common performance bottlenecks on the modern web. They are render-blocking resources, they delay text display, and when handled carelessly, they cause cumulative layout shift — the sudden, jarring reflow of content as fonts swap in after the page has already painted. For a technology that exists to make text look better, fonts have a remarkable capacity to make the entire user experience worse.

The good news is that web font performance is one of the most well-understood optimization domains in frontend development. The techniques are established, the tooling is mature, and most of the gains can be achieved without changing a single line of design. This guide walks through the complete optimization process, from auditing your current state to implementing the specific CSS and HTML attributes that make the difference.


Audit: How Slow Are Your Fonts?

Before optimizing anything, measure what you are starting with. A common mistake is jumping straight to solutions without understanding the actual problem — sometimes fonts are not the bottleneck, and optimizing them will have minimal effect. Other times, a single unoptimized font file is responsible for seconds of perceived latency.

The most useful audit tools are Chrome DevTools' Performance panel and Lighthouse. In DevTools, load your page with a throttled connection (Fast 3G is a useful baseline) and look at the waterfall chart. Identify when font files are requested, when they finish downloading, and what renders in the meantime. Specifically, look for whether your fonts are render-blocking — meaning they delay the First Contentful Paint metric — and whether they cause FOUT (flash of unstyled text) or invisible text periods.

Reading the Waterfall

In the network waterfall, font files typically appear after HTML parsing has begun but before DOMContentLoaded fires. Their download duration depends entirely on file size and connection speed. A typical unoptimized Google Fonts setup might load 3–4 font weight files at 40–80KB each — that is 120–320KB of font data before your user sees styled text on a slow connection.

Lighthouse's "Avoid render-blocking resources" and "Reduce unused CSS" audits will flag font-related issues directly. Pay attention to the "Cumulative Layout Shift" score: if it is above 0.1, fonts are a likely contributor. The Largest Contentful Paint metric is also relevant — if your hero text is in a web font, font loading time directly extends LCP. Understanding font-display behavior is essential context for interpreting these audit results.


Step 1: Switch to WOFF2

WOFF2 is the modern web font format, offering approximately 30% better compression than WOFF (Web Open Font Format 1.0) and 50–60% better compression than raw TTF or OTF files. As of 2024, WOFF2 browser support is effectively universal — 97%+ of global web traffic comes from browsers that support it. There is no practical reason to serve WOFF or TTF files on the modern web.

If you are hosting fonts yourself, ensure all your font files are in WOFF2 format. If you received font files in OTF or TTF format from a foundry or type designer, convert them using the woff2_compress command-line tool from Google's woff2 project, or use a GUI tool like Transfonter or FontSquirrel's Webfont Generator. Be aware that conversion between formats can sometimes slightly alter rendering characteristics due to hinting differences, so always test after converting.

Updating Your @font-face Declarations

A correct WOFF2-optimized @font-face declaration looks like this:

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

Notice that the src descriptor includes only the WOFF2 format. Legacy CSS patterns included WOFF fallbacks: url('...woff2') format('woff2'), url('...woff') format('woff'). This is no longer necessary for any project targeting modern browsers. Removing the WOFF fallback cuts the @font-face declaration complexity in half and removes the possibility of serving the wrong format. For projects that require IE11 support (an increasingly rare requirement), keep the WOFF fallback.

If you are using Google Fonts via the embed URL (@import or <link>), Google already serves WOFF2 to supported browsers automatically. However, the Google Fonts approach introduces third-party DNS lookup latency, which Step 3 addresses.


Step 2: Subset Unused Characters

A full-weight font file typically includes complete character coverage — Latin Extended, Greek, Cyrillic, Vietnamese, mathematical symbols, dingbats, and hundreds of OpenType alternates. For an English-language website, the Latin Extended range is often more than sufficient. Character ranges that will never be used are pure dead weight in every page load.

Subsetting removes glyphs from a font file that your content does not use. For a typical English content site, subsetting to Basic Latin (U+0020–U+007E) plus punctuation and common symbols can reduce font file sizes by 60–80%. A 120KB Inter Regular might become 15–20KB after aggressive subsetting for English-only content.

Subsetting with Glyphhanger and pyftsubset

The most robust tool for font subsetting is pyftsubset from the fonttools Python library:

pip install fonttools brotli
pyftsubset inter-regular.ttf \
  --output-file=inter-regular-subset.woff2 \
  --flavor=woff2 \
  --unicodes="U+0020-007E,U+00A0-00FF,U+2018,U+2019,U+201C,U+201D,U+2026,U+2013,U+2014"

This command subsets the font to Basic Latin, Latin-1 Supplement (accented characters for French, German, Spanish, etc.), and the most common typographic punctuation: curly quotes, ellipsis, en dash, and em dash. For many projects, this covers 99%+ of actual content.

Glyphhanger, a Node.js tool by Zach Leatherman (creator of the Eleventy static site generator), takes a more automated approach: it crawls your website and creates a subset based on the characters it actually finds in your content. This is the most precise approach for static sites where the character inventory is known at build time.

npm install -g glyphhanger
glyphhanger https://yoursite.com --subset=inter-regular.ttf --formats=woff2

If your site is multilingual, subsetting requires more care. Use the unicode-range CSS descriptor to serve different subset files for different script ranges, loading only the script the user's content actually requires.


Step 3: Preload Critical Fonts

Font preloading using the <link rel="preload"> directive tells the browser to begin downloading a font file as soon as the HTML is parsed — before the CSSOM is built, before the render tree is constructed, and before the browser has even encountered the @font-face declaration in your stylesheet. This can shave hundreds of milliseconds from perceived text render time.

The preload directive belongs in your HTML <head>, as early as possible:

<link
  rel="preload"
  href="/fonts/inter-regular.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

The crossorigin attribute is required even for same-origin fonts — browsers fetch font resources in a special CORS mode, and omitting crossorigin causes the browser to fetch the font twice (once for the preload, once for the actual use). This is a common mistake that negates the benefit of preloading.

What to Preload and What Not to Preload

Preloading is most valuable for fonts that appear in the above-the-fold content: your primary heading font, your body font, and any fonts used in navigation or hero sections. Preloading every font on the page is counterproductive — it increases network contention and may delay other critical resources.

A practical rule: preload only the specific weights and styles that appear in the first screenful of content. If your body text uses Inter Regular and your headings use Inter Bold, preload both of those files. If you also use Inter Italic in pull quotes that appear further down the page, do not preload the italic — let it load on demand. The Roboto family has four weights commonly used together (300, 400, 500, 700), but only 400 and 700 are typically above the fold. Preload just those two.

For Google Fonts users, preloading becomes more nuanced because the font file URL is not known until the CSS is parsed. The recommended approach is to host fonts locally (self-hosting), which gives you full control over preload URLs and eliminates the third-party DNS lookup.


Step 4: Set the Right font-display Value

The font-display CSS descriptor controls what the browser shows during font loading. It is one of the highest-leverage controls you have over perceived performance, and the wrong value can make a fast font load feel slow or cause significant layout shift from fonts.

The five possible values are:

  • auto — browser default behavior (typically equivalent to block)
  • block — invisible text for a short period, then the web font
  • swap — fallback font immediately, then the web font swaps in when ready
  • fallback — very brief invisible period, then fallback, then the web font (if loaded within ~3 seconds)
  • optional — same as fallback, but the browser may skip the swap if the network is slow
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

For most content websites, font-display: swap is the correct choice. It shows text immediately in a fallback font (good for perceived performance) and swaps to the web font when it loads (good for brand consistency). The tradeoff is that the swap can cause FOUT — a brief flash as the layout reflows when the web font arrives. This is generally preferable to invisible text.

Choosing Based on Use Case

For apps where layout shift is particularly disruptive — data tables, form-heavy interfaces, dashboards — consider font-display: optional. This approach treats the web font as a progressive enhancement: if it is cached from a previous visit, it will be used; if not, the fallback font is used permanently for that page load. The user never experiences a jarring reflow, but first-time visitors always see the fallback. This is the approach Google uses on google.com itself.

For brand-critical display headings on marketing pages where the visual impression is paramount, font-display: fallback offers a middle ground: a brief invisible period (100ms) followed by fallback text, with the web font swapping in if it loads within 3 seconds. Beyond 3 seconds, the user continues with the fallback — a sensible cutoff.


Step 5: Match Fallback Font Metrics

Even after implementing everything above, there is a subtle performance problem that many developers overlook. When font-display: swap causes the web font to replace the fallback font, the two fonts almost certainly have different metrics — different sizes, different line heights, different character widths. This causes layout shift: paragraphs move, buttons resize, the page jumps. This is the primary driver of poor CLS scores on sites using web fonts.

The modern solution is CSS size-adjust, ascent-override, descent-override, and line-gap-override — a set of @font-face descriptors that let you warp your fallback font's metrics to match your web font's metrics. The result is that when the web font swaps in, the layout does not change, because both fonts occupy the same space.

/* Adjusted Arial fallback to match Inter metrics */
@font-face {
  font-family: 'Inter-fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: 'Inter', 'Inter-fallback', sans-serif;
}

The specific values depend on the font pair. Tools like the Automatic Font Matching tool by Katie Hempenius calculate the correct values automatically — you provide your web font and your intended fallback, and the tool generates the @font-face override descriptor values.

Generating Override Values Programmatically

For build-pipeline automation, the fontpie npm package calculates fallback font adjustments from font files directly:

npx fontpie inter-regular.woff2 --name "Inter"

It outputs the CSS @font-face block with all four override descriptors pre-calculated. This can be integrated into build scripts to automatically regenerate fallback metrics whenever font files are updated. The guide to preventing layout shift with fonts covers this technique in depth with more examples.


The Complete Font Performance Checklist

Bringing everything together, here is the ordered checklist for a fully optimized web font implementation. Work through it top-to-bottom on any new project, and revisit it during performance audits on existing projects.

Format: Serve all fonts as WOFF2. Remove legacy WOFF/TTF entries from @font-face declarations unless you have documented IE11 requirements.

Subset: Reduce each font file to the character range your content actually uses. Target under 20KB per weight for Latin content sites. Use pyftsubset or Glyphhanger for automated subsetting.

Host locally: Self-host font files to eliminate third-party DNS lookups and gain control over caching and preload URLs. Add Cache-Control: max-age=31536000, immutable headers to font files — they never change for a given URL if you version file names correctly.

Preload above-fold fonts: Add <link rel="preload" as="font" crossorigin> for each font weight used in the first viewport. Limit preloads to 2–4 fonts.

Set font-display: Use swap for content sites, optional for app interfaces where layout stability is paramount. Never leave it unset (which defaults to block and causes invisible text).

Match fallback metrics: Use size-adjust and *-override descriptors to create an adjusted fallback font that prevents layout shift during the swap. Verify with Lighthouse CLS score — target below 0.1.

Measure after changes: Re-run Lighthouse after implementing each step. Track Largest Contentful Paint, Cumulative Layout Shift, and Total Blocking Time. The web font performance guide provides a more detailed benchmarking methodology. For subsetting techniques, see the font subsetting guide. For preloading specifics, the font preloading guide covers advanced scenarios including HTTP/2 push alternatives and resource hints for third-party fonts.

Typography Terms

Try These Tools

Fonts Mentioned

Related Articles