Web Developer Font Conversion Guide: TTF, OTF, WOFF, and @font-face
- Web fonts should be WOFF (or WOFF2) for optimal browser delivery
- Use a browser-based converter for TTF/OTF/WOFF — no upload needed
- Always use font-display: swap and fallback fonts in @font-face
- Subset fonts to include only needed characters — reduces size by 50-90%
Table of Contents
Self-hosting fonts gives you full control over delivery, privacy (no third-party CDN), and performance. But it requires understanding font formats, conversion, and the right @font-face setup. This guide covers everything a web developer needs to go from a TTF download to optimized, self-hosted web fonts.
Which Font Formats to Generate for Your Website
For 2026 web projects, generate two formats per font: WOFF2 as the primary and WOFF as the fallback. This covers 99.9%+ of browser traffic:
- WOFF2 — Supported by all modern browsers (Chrome 36+, Firefox 39+, Safari 12+, Edge 14+). Uses brotli compression: ~50% smaller than TTF. This is what your browser will use in almost all cases.
- WOFF — Broader compatibility fallback (IE9+). Uses zlib compression: ~30-40% smaller than TTF. Your fallback for the rare browser that doesn't support WOFF2.
TTF as a web font is wasteful — it's uncompressed and can be 2x the size of WOFF2. Only serve TTF if you specifically need IE8 support (which you almost certainly don't in 2026).
The Font Converter generates WOFF from TTF or OTF directly in your browser. For WOFF2, use fonttools: pip install fonttools brotli && fonttools convert input.ttf output.woff2.
The Correct @font-face CSS Declaration
Here's the production-ready @font-face declaration for a self-hosted font with WOFF2 and WOFF fallback:
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont-regular.woff2') format('woff2'),
url('/fonts/myfont-regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+0152-0153; /* Optional */
}
Key points:
- font-display: swap — Shows fallback text immediately while the web font loads, then swaps. Prevents invisible text (FOIT). Use
optionalinstead if the font is decorative and you'd rather skip it on slow connections. - font-weight and font-style — Be explicit. Without these, browsers may apply faux bold or faux italic to compensate for a missing weight.
- unicode-range — Optional but powerful. Tells the browser to only load this font file when it encounters characters in the specified range. If a page has no Latin Extended characters, the browser skips loading that font file entirely.
- Format order — WOFF2 first. Browsers use the first format they support.
Reduce Font File Size With Subsetting
Most fonts include thousands of glyphs — mathematical symbols, currency characters, Latin Extended, Cyrillic, Greek, etc. If your site is English-only, you're serving characters your users will never see.
Subsetting removes unused characters from the font file. A typical Latin-subset of a large font can go from 200KB to under 30KB — an 85%+ reduction. This has a dramatic effect on page load time, especially on mobile.
The Font Subsetter on WildandFree Tools lets you specify exactly which characters to keep (paste the characters, or choose a standard subset like Basic Latin) and downloads a reduced font file. Use this on TTF or OTF before converting to WOFF/WOFF2.
For programmatic subsetting: pip install fonttools, then:
pyftsubset input.ttf --output-file=subset.ttf --unicodes="U+0020-007F,U+00A0-00FF" --layout-features="*"
The --layout-features="*" flag preserves OpenType features like ligatures. Without it, pyftsubset removes them.
Check Font Licenses Before Self-Hosting
Web embedding is a specific license grant that not all fonts include. Common license categories:
- SIL OFL (Open Font License) — Free for any use including web embedding. No attribution required in most cases. Google Fonts uses this for most of their catalog.
- Desktop license — Many commercial fonts include only desktop installation rights. Web embedding requires a separate "web font" license, usually sold by the foundry as a page-view-based subscription.
- Free for personal use — Personal use licenses typically do not cover commercial websites. Check before using on a client site or any site with commercial intent.
The font file contains embedded license metadata. The Font Metadata Viewer shows the license text (name ID 13), license URL (name ID 14), and fsType embedding flags — the latter specifically indicates whether web embedding is permitted. A fsType value of 0 means fully embeddable; values like 4 or 8 indicate restrictions.
Variable Fonts: One File for All Weights
Variable fonts pack all weights, widths, and optical sizes into a single file using OpenType variation axes. A single variable TTF can replace 4-8 static font files with one file that's typically 40-60% smaller than the full static family.
For web use, variable fonts are served exactly like static fonts — just with a broader @font-face declaration:
@font-face {
font-family: 'MyVariableFont';
src: url('/fonts/myfont-variable.woff2') format('woff2 supports variations');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
The font-weight: 100 900 range tells the browser this font covers the full weight spectrum. Use any weight in CSS: font-weight: 350, font-weight: 680, etc.
Variable font conversion is complex — the browser-based font converter handles standard static fonts best. For variable fonts, fonttools provides specific tools (instancer for creating static instances) if you need to manipulate them.
Convert Your Fonts for Web — Free Browser Tool
TTF to WOFF in your browser — no upload, no server, no account. Your font files stay on your machine throughout.
Open Font ConverterFrequently Asked Questions
Should I use Google Fonts CDN or self-host?
Google Fonts CDN is convenient but loads fonts from a third-party server (a privacy concern in some regions, particularly relevant for GDPR compliance in the EU). Self-hosting fonts gives you control over the request origin, cache headers, and delivery performance. For performance, self-hosting with proper font-display and subsetting is typically as fast or faster than the Google CDN.
How do I check if my self-hosted font is loading correctly?
Open browser DevTools → Network tab → reload the page. Filter for "font" type requests. You should see your WOFF2 or WOFF files loading with a 200 status. Check that the Content-Type header is "font/woff2" or "font/woff". Also check the Timing tab for load time — good web fonts load in under 200ms on a typical connection with proper caching headers (Cache-Control: max-age=31536000).
What is the best font-display value for web fonts?
font-display: swap is the standard recommendation for body text and headings — it ensures text is visible immediately using a fallback font while the web font loads. For decorative or display fonts where the specific font matters more than instant visibility, font-display: block or font-display: optional may be better. swap prevents CLS (Cumulative Layout Shift) if you size your fallback font similarly to your web font.
How many web font files is too many?
Every @font-face file is a separate HTTP request. For most sites, 2-4 font files (regular, bold, italic, bold italic of one family) is manageable. More than 6-8 font files meaningfully affects page load performance, especially on first visit before caching kicks in. Variable fonts solve this by combining all weights into one file.

