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 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 (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
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 (free, then host yourself)
- Fontshare (maintained by Indian Type Foundry)
- Fontsource (pre-packaged for easy hosting)
- V-fonts.com
- Directly from the foundry of your choice
Step 2: Add the 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
.heading {
font-weight: 700; /* bold */
}
.light-text {
font-weight: 300; /* light */
}
Step 4: Optimize loading
Add this to your <head> to preload the font:
<link rel="preload" as="font" href="/fonts/inter-variable.woff2" type="font/woff2" crossorigin>
Plan B: If you don’t have the web space to host fonts
If hosting files yourself feels like extra overhead, Bunny Fonts gives you a Google Fonts mirror without the privacy baggage. It's still faster than Google's CDN in many regions.
<link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
<link href="https://fonts.bunny.net/css?family=roboto:400,700" rel="stylesheet">
<style>
body {
font-family: 'Roboto', sans-serif;
}
</style>
Font swap and fallbacks
When your custom font loads, the browser has to decide what to display while waiting. This is called font-display.
@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 secondsoptional- 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.
@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. It has pre-built combinations of system fonts that look great and have no loading penalty at all.
Example:
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
That's it.