mgx

why self-host fonts (woff2 & variable)

Yeah. This might be common knowledge to many, but hey.. I find myself explaining this to new collaborators fairly often, so I'm putting it together as an article I can point them to. It might be useful for you as well. Whether you’re a hobbyist web developer, a tinkerer, or simply curious about optimizing your site, the steps and reasoning outlined here are both practical and easy to follow. ## The case for self-hosting **Google fonts issues:** - You're relying on a third-party, which can be slow or blocked in certain regions - Google sees every visitor who loads your fonts (privacy concern if you care about that) - One more request to another domain = [one more DNS lookup](https://static.mighil.com/images/2025/1763638828044_no-time-busy.gif) and connection overhead **Self-hosting means:** - Your fonts load from your own server (faster, fewer moving parts) - No third party tracking who visits your site ## Why WOFF2 (and variable fonts) WOFF2 is significantly smaller than other formats -- usually 30% smaller than regular WOFF. It's supported in all modern browsers, so there's no reason to keep serving older formats. [Variable fonts](tab:https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Fonts/Variable_fonts) (when optimized) are even smarter. Instead of serving separate files for bold, italic, thin, etc., one variable font file handles all the weights and styles you need. This means fewer HTTP requests and a smaller total file size. ``` // Example Regular WOFF: Inter-Regular.woff (50 KB) Bold WOFF: Inter-Bold.woff (52 KB) Total: 102 KB, 2 requests Variable WOFF2: Inter-Variable.woff2 (65 KB) Total: 65 KB, 1 request ``` Learn: [How To Convert Variable TTF Font Files to WOFF2](tab:https://henry.codes/writing/how-to-convert-variable-ttf-font-files-to-woff2/) ## Quick start to self-host your fonts ### Plan A **Step 1: Get your fonts** Download variable fonts in WOFF2 format from sources like: - [Google Fonts](https://fonts.google.com/) (free, then host yourself) - [Fontshare](https://www.fontshare.com/) (maintained by Indian Type Foundry) - [Fontsource](https://fontsource.org/) (pre-packaged for easy hosting) - [V-fonts.com](https://v-fonts.com/licenses/free-for-commercial-use) - Directly from the foundry of your choice **Step 2: Add the CSS** ```css @font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-weight: 100 900; /* variable fonts support a range */ font-variation-settings: "wght" 400; /* optional: set default weight */ } body { font-family: 'Inter', system-ui, sans-serif; } ``` **Step 3: Use font weights freely** ```css .heading { font-weight: 700; /* bold */ } .light-text { font-weight: 300; /* light */ } ``` **Step 4: Optimize loading** Add this to your `` to preload the font: ```html ``` ### Plan B: If you don’t have the web space to host fonts If hosting files yourself feels like extra overhead, [Bunny Fonts](tab:https://bunny.net/fonts) gives you a Google Fonts mirror without the privacy baggage. It's still faster than Google's CDN in many regions. ```html ``` ## Font swap and fallbacks When your custom font loads, the browser has to decide what to display while waiting. This is called **font-display**. ```css @font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-display: swap; /* show system font immediately, swap when custom font loads */ } ``` **font-display options:** - `swap` - Show system font now, replace when ready (best for web fonts) - `block`- Blank text until font loads (only use for icon fonts) - `fallback`- Blank for 100ms, then show system font; switch to custom font if it loads within 3 seconds - `optional`- Show system font, only use custom font if it's already cached For web fonts, **`swap` is the move**. Users see text immediately, then it switches when your font arrives. The UX is way better than waiting for blank space. ```css @font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-weight: 100 900; font-display: swap; /* always use this */ } body { font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } ``` The fallback fonts (`system-ui`, `-apple-system`, etc.) are system fonts that look reasonable until your custom font loads. The browser uses the first one it recognizes. ## Don't need custom fonts? If you want a clean, simple stack that works everywhere without extra requests, check out [Modern Font Stacks](https://github.com/system-fonts/modern-font-stacks). It has pre-built combinations of system fonts that look great and have no loading penalty at all. Example: ```css body { font-family: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, serif; } ``` ## TL;DR - Use **WOFF2** (smaller, works everywhere) - Use **variable fonts** (one file, all weights) - Self-host or use **Bunny Fonts** (faster, privacy-friendly) - Set `font-display: swap` (users see text fast) - Fallback to system fonts gracefully (no blank text) - Or skip custom fonts entirely and use [system font stacks](https://github.com/system-fonts/modern-font-stacks) That's it.

Tagged in tech