How to convert ttf to woff2 online for free
- Step 1Drop the font onto the converter — Drag a `.ttf`, `.otf`, `.woff`, or `.woff2` file onto the drop zone, or click to browse. The picker keeps a single file (this tool is one-file-at-a-time — there is no multi-file queue in the web UI). The accept filter is `.ttf,.otf,.woff,.woff2`.
- Step 2Let the size + format check run — Before processing, the file is checked against your tier limit — **5 MB Free, 50 MB Pro, 1 GB Developer** — and against the supported extensions. A `.ttc` collection or an unrelated file is rejected here with `not a supported font (TTF/OTF/WOFF/WOFF2)`.
- Step 3Press Process to run the WASM encoder — `wawoff2` instantiates its WebAssembly module on first use, then `wawoff2.compress()` runs on the decoded SFNT. It executes on the main thread (not a Web Worker), so a very large CJK font can briefly block the tab while Brotli runs.
- Step 4Read the result metrics — The panel reports `Source format`, `WOFF2 size`, original vs output bytes, and a green `Saved: N%` reduction when the output is smaller. For an already-compressed WOFF2 input the reduction may be ~0% — that is expected.
- Step 5Download the WOFF2 — Click Download — the file is `<stem>.woff2` with MIME `font/woff2`. The download is generated from an in-memory `Blob`; no network request leaves your machine.
- Step 6Wire it into your @font-face — Point your `src` at the new file: `src: url("/fonts/name.woff2") format("woff2")`. Generate the full block with the [Font Face Generator](/font-tools/font-face-generator), or preload the critical weight with the [Preload Tag Builder](/font-tools/preload-tag-builder).
What the converter accepts and emits
Behaviour grounded in the implementation: input format is sniffed from the first 4 magic bytes, then normalised to an SFNT before Brotli compression. There are no conversion options.
| Input | Detected by | Internal step | Output |
|---|---|---|---|
.ttf (TrueType) | magic 0x00010000 (or true) | Passed straight through as SFNT | <stem>.woff2, font/woff2 |
.otf (OpenType/CFF) | magic OTTO (0x4F54544F) | Passed straight through as SFNT | <stem>.woff2, font/woff2 |
.woff (WOFF 1.0) | magic wOFF (0x774F4646) | Each table zlib-inflated, rebuilt into a flat SFNT, then re-compressed | <stem>.woff2, font/woff2 |
.woff2 (WOFF 2.0) | magic wOF2 (0x774F4632) | Decompressed to SFNT via wawoff2, then re-compressed | <stem>.woff2 (often ~0% change) |
.ttc (TrueType Collection) | magic ttcf (0x74746366) | Not handled — fileToSfntBuffer throws | Error: Unsupported font format: ttc |
Per-tier file-size limits (web UI)
The converter only enforces a per-file byte limit; there is no glyph-count or batch gate on this specific tool in the browser. Limits mirror lib/tier-limits.ts.
| Tier | Max file size | Batch in web UI | Notes |
|---|---|---|---|
| Free | 5 MB (5,000,000 bytes) | 1 file | Covers nearly all single-weight Latin/EU fonts pre-compression |
| Pro | 50 MB (50,000,000 bytes) | 1 file | Comfortable for large CJK desktop fonts |
| Developer | 1 GB (1,000,000,000 bytes) | 1 file | Headroom for unusual multi-script masters |
| Over limit | — | — | Blocked with exceeds the <tier> tier per-job limit before any compression runs |
Cookbook
The exact lifecycle of a file in this tool, plus the copy-paste @font-face it feeds. No options exist to configure — these are the real before/after shapes.
TrueType in, WOFF2 out
ExampleThe common path. A desktop Inter weight at 300 KB drops to roughly 90–110 KB as WOFF2. The output is named from the input stem.
Input: Inter-Regular.ttf (≈ 300 KB, magic 00 01 00 00) Step: detected ttf → SFNT passthrough → wawoff2.compress() Output: Inter-Regular.woff2 (≈ 90–110 KB, magic 77 4F 46 32 'wOF2') Metrics panel: Source format: TTF · Saved: ~65%
OpenType/CFF (.otf) in, WOFF2 out
ExampleCFF (PostScript-outline) fonts convert identically — the magic OTTO is detected and the SFNT is wrapped. The flavor (CFF vs TrueType) rides through unchanged.
Input: SourceSerif.otf (magic 4F 54 54 4F 'OTTO') Output: SourceSerif.woff2 # The browser unwraps to the same CFF SFNT at parse time.
Re-encoding an existing WOFF to WOFF2
ExampleGot a legacy WOFF 1.0 and want WOFF2? Drop the .woff in — it is zlib-inflated table by table, rebuilt into an SFNT, then Brotli-compressed. Typically another 25–35% smaller than the WOFF.
Input: oldfont.woff (magic 77 4F 46 46 'wOFF', zlib per table) Step: inflate each table → flat SFNT → wawoff2.compress() Output: oldfont.woff2 (Brotli + glyf/loca transform)
The @font-face it feeds
ExampleWOFF2-only is the right modern shipping shape. List nothing else unless analytics show real IE11 traffic — then add a WOFF via TTF to WOFF.
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/Inter-Regular.woff2") format("woff2");
}Confirming the result really is WOFF2
ExampleThe first four bytes are the proof. xxd (or the Font Format Identifier) shows the wrapper without parsing anything else.
$ xxd -l 4 Inter-Regular.woff2 00000000: 774f 4632 wOF2 # 77 4F 46 32 = 'wOF2' → WOFF 2.0
Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
Uploading a .ttc collection (or a .ttc renamed to .ttf)
Rejected — unsupportedA real .ttc is rejected by the extension check (not a supported font). If it is renamed to .ttf, the magic-byte sniff still reads ttcf and fileToSfntBuffer throws Unsupported font format: ttc. Split the collection into individual TTF/OTF faces with a desktop tool (e.g. fonttools ttx / otc2otf) first, then convert each face separately.
Re-converting a file that is already WOFF2
Expected — near-zero changeA .woff2 input is decompressed to SFNT and re-compressed. Because Brotli is deterministic-ish on the same encoder, the output is roughly the same size — the Saved: N% chip may show 0% (it only appears when the reduction is positive). This is not a failure; it is the converter doing a faithful round-trip.
File is larger than your tier limit
Blocked — 5 MB / 50 MB / 1 GBThe size check runs before compression. A 12 MB CJK TTF on Free fails with exceeds the free tier per-job limit (5.0 MB). Subset the font first with the Font Subsetter or Latin Filter to drop under the cap, or upgrade the tier.
CJK font compresses far less than a Latin font
By designMost of a CJK font's bytes are glyph outlines that are already information-dense, so WOFF2 typically saves ~25–35% on CJK versus ~60% on Latin. The output is still valid and smaller — there is no error. For a dramatic shrink you must subset to the characters you actually use, not just re-wrap.
Output is bigger than a hand-tuned woff2_compress build
Expected — same engine, default settingsThis tool uses wawoff2 at its default Brotli quality. Command-line woff2_compress uses the same algorithm; differences of a few percent come from Brotli quality/window tuning, not from a different format. The result is a standards-compliant WOFF2 either way.
A corrupt or truncated SFNT
Error — invalid inputIf the bytes past the magic don't form a valid SFNT table directory, wawoff2.compress() returns a falsy value and the run surfaces an error in the red panel. Re-export the font from its source app, or run the Font Format Identifier to confirm what the file actually is.
Variable font (fvar/gvar) conversion
SupportedWOFF2 wraps the whole SFNT including fvar/gvar/STAT, so a variable font converts losslessly and the axes still work after the browser decompresses. The glyf/loca transform applies; variation tables ride along in the Brotli stream. (To bake a single static instance instead, use the Variable Font Freezer.)
Colour font (COLR/CPAL, sbix, CBDT) conversion
PreservedWOFF2 is table-agnostic: COLR/CPAL, sbix (Apple bitmap), CBDT/CBLC (Google bitmap), and SVG tables are all carried through unchanged. If the converted font renders monochrome it is a renderer-support question, not a data-loss one — the colour tables are in the WOFF2.
Frequently asked questions
Is anything uploaded to a server?
No. The file is read with file.arrayBuffer() and compressed by the wawoff2 WebAssembly module running in your browser tab. The result panel shows a 0 bytes uploaded badge precisely because nothing leaves the machine. The only server contact is a single anonymous usage counter for signed-in dashboard stats — no font bytes.
Is the WOFF2 conversion lossless?
Yes, for the font's table data. WOFF2 wraps the original SFNT tables with Brotli compression plus a glyf/loca transform. The browser reverses both at parse time to reconstruct an in-memory SFNT equivalent to the source — same glyph outlines, metrics, kerning, hinting, and OpenType features. The optional metadata/private blocks are the only things a wrapper can drop.
Can I convert OTF as well as TTF?
Yes. The tool detects OTTO (CFF/PostScript-outline OpenType) and 0x00010000 (TrueType-outline) and wraps either into WOFF2. CFF outlines convert exactly the same way; nothing about CFF prevents WOFF2 packaging.
Does it accept WOFF or WOFF2 as input?
Yes. A .woff is zlib-inflated table by table into a flat SFNT and then Brotli-compressed; a .woff2 is decompressed to SFNT and re-compressed. So you can use this tool to upgrade WOFF to WOFF2, or to round-trip a WOFF2.
What's the maximum file size?
Free tier: 5 MB. Pro: 50 MB. Developer: 1 GB. The check runs before compression, so an over-limit file is blocked immediately rather than after a long Brotli pass. Most fonts are well under 1 MB even before compression.
Are there any conversion options or quality settings?
No. This tool has an empty options schema — drop a file, press Process, download. wawoff2.compress() runs at its default Brotli settings. If you need to shrink further, that's a subsetting job, not a quality dial: use the Font Subsetter.
Why isn't the file getting much smaller?
Two common reasons. (1) The input is already a WOFF2 — re-compressing an already-compressed file yields ~0%. (2) It's a CJK or icon font whose bytes are mostly dense glyph data, where WOFF2 saves ~25–35% rather than ~60%. To really cut bytes, subset to the glyphs you use.
Does it run in a background worker?
No — the compression runs on the page's main thread when you press Process. For small and mid-size fonts this is instant; a very large CJK font can briefly freeze the tab while Brotli runs. That's a UI characteristic, not a correctness issue.
Does WOFF2 change how my font renders?
No. The decompressed SFNT is equivalent to the source, so glyphs, kerning, ligatures, and hinting are identical. WOFF2 is a wire-format optimisation — the browser renders from the reconstructed SFNT, not from the compressed bytes.
Why not just gzip or zip the TTF?
Browsers don't decompress a raw .zip into a usable font, and transport gzip doesn't reorganise the glyph data. WOFF2 is a font-aware container the browser natively loads, and its glyf/loca transform lets Brotli compress glyph outlines far better than generic gzip on the raw TTF.
What browser support does WOFF2 have in 2026?
Over 99% globally. Every modern browser since Chrome 36, Firefox 39, Safari 10, and Edge 14 supports WOFF2. The only notable gaps are Internet Explorer 11 and a few discontinued mobile browsers — for those, ship a WOFF fallback.
Can I automate this for a whole fonts folder?
The web UI is one file at a time. For a folder, run the same engine in your build (the wawoff2/woff2_compress family), or use the JAD runner's local HTTP API at http://127.0.0.1:9789/v1/tools/ttf-to-woff2/run. The full CI patterns are in Automate TTF to WOFF2 in your build pipeline.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.