Pick Your Dev Method: Block, Hybrid, or Classic?
How you self-host fonts depends entirely on the type of WordPress theme you’re building. If you are creating a Block Theme – great news! You don’t have to read this article! WordPress now lets you add and manage custom fonts directly in the Site Editor under Styles → Typography.
However, if you’re working with a Hybrid Theme or a Classic Theme, you’ll need to do a little more hands-on work:
- Download your fonts
- Convert
.TTFfont files to.WOFF2files - Load them cleanly through
functions.phpandtheme.json
However you decide to build your theme (or non-WordPress website), you will want to make sure that you are optimizing the technical aspects of the site as well as it’s content. Including external fonts locally, within your theme, is an easy win.
Why is it important to self-host fonts?
- Faster page loads
- Better Core Web Vitals
- Full control over privacy and design
Say Hello to Variable Fonts
Variable fonts allow for greater design control with optimized performance. Using a single, variable font file we can use CSS to adjust properties like font-stretch, font-style, and font-weight. But, including variable fonts can be a little tricky. If you’ve downloaded .ttf files for a variable font from Google Fonts and have gotten stuck because it’s not rendering correctly, it’s not just you. The devil is (so often) in the details.
Learn more about using variable fonts in WordPress themes.
In this example, I’m using a Hybrid Theme approach. Let’s walk through exactly how to self-host variable Google Fonts correctly — and how to fully support Gutenberg editor styles too.
Why .TTF Fonts Aren’t The Best Choice for the Web
First, .ttf (TrueType) fonts were never designed for the web:
- They’re huge compared to
.woff2(30–50% bigger). - They don’t stream efficiently, slowing down first paint.
- They carry extra metadata browsers don’t need.
Modern browsers love .woff2 because it’s lightweight and streamable. If you want fast fonts, you must convert your .ttf files.
How to Convert .TTF Files to .WOFF2
After testing several tools, the best converter I tried was Everything Fonts TTF to WOFF2 tool. It delivered clean, minimal .woff2 files — no bloated metadata, no weird rendering issues.
💡 Pro Tip:
Make sure you’re converting variable .ttf files if you want full weight ranges (100–900), not just individual weights. While you can convert any .ttf font file, if it is not a true variable font, you won’t get all of the benefits. This process will not magically create a variable font for you.
Setting Up Local Font Hosting in Your WordPress Theme
You’ve selected the variable fonts that you want to use and you’ve download the files. Next, you’ll want to convert the .ttf files to .woff2 files, as mentioned above. I’ll be using REM and Nunito, both variable Google Fonts in this example. Here’s how you can now include your fonts in your WordPress theme:
1. Organize Your Fonts in Your Theme Folder
Store your converted fonts neatly inside your theme:
/assets/fonts/nunito/Nunito-VariableFont_wght.woff2
/assets/fonts/rem/REM-VariableFont_wght.woff2
✅ One folder per font family keeps things clear and maintainable.
2. Create a Fonts CSS File
Create an /assets/css/fonts.css with proper @font-face declarations:
@font-face {
font-family: 'REM';
src: url('../fonts/rem/REM-VariableFont_wght.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Nunito';
src: url('../fonts/nunito/Nunito-VariableFont_wght.woff2') format('woff2');
font-weight: 200 1000;
font-style: normal;
font-display: swap;
}
✅ Always use font-display: swap for faster visible text.
3. Enqueue fonts.css in functions.php
Load your local fonts CSS using wp_enqueue_style:
/**
* Enqueue theme styles and fonts.
*/
function mytheme_enqueue_styles() {
wp_enqueue_style(
'mytheme-local-fonts',
get_template_directory_uri() . '/assets/css/fonts.css',
array(),
wp_get_theme()->get( 'Version' )
);
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_styles' );
✅ This makes fonts available globally on the frontend.
4. Register Fonts in theme.json (with src!)
This step is critical. Without a src reference inside theme.json, Gutenberg won’t know where to find your local fonts in the Block Editor.
Here’s the correct setup:
{
"settings": {
"typography": {
"fontFamilies": [
{
"fontFamily": "REM, sans-serif",
"name": "REM",
"slug": "rem",
"fontFace": [
{
"fontFamily": "REM",
"fontStyle": "normal",
"fontWeight": "100 900",
"src": [
"file:./assets/fonts/rem/REM-VariableFont_wght.woff2"
]
}
]
},
{
"fontFamily": "Nunito, sans-serif",
"name": "Nunito",
"slug": "nunito",
"fontFace": [
{
"fontFamily": "Nunito",
"fontStyle": "normal",
"fontWeight": "200 1000",
"src": [
"file:./assets/fonts/nunito/Nunito-VariableFont_wght.woff2"
]
}
]
}
]
}
}
}
✅ Notice each fontFace block includes a local src path to the .woff2.
✅ The Block Editor and the frontend now both load the same fonts — no mismatch warnings.
Do You Still Need Static Fonts as Fallback?
For 99% of projects today? No.
All modern browsers — Chrome, Firefox, Safari, Edge — support .woff2 variable fonts. Adding static .woff2 or .ttf backups only bloats page weight.
Only consider static fallbacks if:
- You have strict enterprise requirements for ancient browsers.
- Your analytics show a significant number of IE11 or Android 5 users.
Otherwise, skip static fallbacks and stay light.
In Conclusion
Self-hosting fonts is a must for performance optimized WordPress web development. By converting fonts correctly and setting up theme.json the right way, you’ll get:
- Faster loads
- Stronger SEO
- Better design control
When converting .ttf to .woff2, I liked Everything Fonts — it simply worked better than the others for me.
Learn more about the benefits of block themes and explore a recent case study, migrating a client site from a commercial builder theme framework to a custom block theme.
Have questions or something you got stuck on? Leave a comment or feel free to contact me.

