Adding a custom font using a child theme – self-hosting is essential for best performance and removes privacy considerations.
Choosing a font for self-hosting
Google font
Find and download the font from Google Fonts. If it’s a “Variable” font, you’ll probably want a single font file such as PlayfairDisplay-VariableFont_wght.ttf and optionally PlayfairDisplay-Italic-VariableFont_wght.ttf.
Inter variable font
Inter is a really popular modern font you can get from Google Fonts (or Bunny Fonts), load it from rsms.me/inter CDN sponsored by Cloudflare or self-host it. To self-host, first download it from https://rsms.me/inter/download/ and find the InterVariable.woff2 from the web directory of the zip file.
Custom font
If you have a font from another source they usually come with specific instructions about which files and CSS to use – adapt it accordingly.
System font
Do you even need a font? Best performance and least hassle – try a system font from System Font Stack. These will look slightly different depending on the operating system the user is using, how ever.
Self-hosting a font
For this example, let’s set up the Inter variable font.
1. Prepare the child theme
Create a fonts directory in your child theme folder (comes by default with ska-theme-child) and place the InterVariable.woff2 file in there. Optionally add an .htaccess file (if you’re using Apache web server):
# Serve fonts with an efficient cache policy
<IfModule mod_headers.c>
<FilesMatch "\.(otf|ttf|woff|woff2)$">
Header set Cache-Control "max-age=315360000, public"
</FilesMatch>
</IfModule>Now we need this font loaded on the front end but also in the Block editor and TinyMCE editor (classic editor, for editing post types that don’t support Gutenberg, such as WooCommerce products). This can either be achieved by modifying the child theme’s style.css file or via hooks in the child theme’s functions.php file.
2. Load the self-hosted font
Child theme’s style.css is the best place to put your @font-face because with ska-theme-child the child theme’s style.css is “inlined” by default – no extra file request on the front end.
/**
* Theme Name: ska-theme-child
* Template: ska-theme
* Version: 1.0.1
*/
@font-face {
font-family: 'Inter';
src: url('/wp-content/themes/ska-theme-child/fonts/InterVariable.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}Increment the Version of the child theme header comment to ensure cache busting.
3. Pre-load the font
It’s a good idea to tell the browser as soon as possible to start fetching the font in order to minimize FOUT (Flash of Unstyled Text).
add_action('wp_head', function() {
echo '<link rel="preload" as="font" href="/wp-content/themes/ska-theme-child/fonts/InterVariable.woff2" type="font/woff2" fetchpriority="high" crossorigin="anonymous">';
}, 5);Be sure to use crossorigin="anonymous" even though the font is served from your own domain because otherwise the browser may not treat the URL specified in the style.css the same as the one in the <link> and produce 2 network requests that take additional time to sort it out.
It’s not render-blocking, just a strong hint to the browser to get the font downloaded ASAP.
4. Update Tailwind configuration
Finally you should update Tailwind typography settings to no longer use an external font.
- Navigate to ska-blocks -> Tailwind -> Typography.
- Find the font you’re self-hosting and click “Edit“.
If you’re adding a brand new font just click the plus icon. - Change the font source to “No external font“.
- Enter the font name manually as the first item under “Local fonts” (the rest are fallback fonts).
This can be done by clicking a plus icon of the first row and then clicking an up arrow icon to move the newly created row to the first position.
Fonts that have a name with spaces should be wrapped in double quotes (").
A custom font can include another font as a fallback by using var(--font-{slug}) as a fallback font:
Optimizing further
Subset your fonts
Subsetting a font to only include the characters that your site needs can reduce its’ size by 5-10x.
Alternatively ensure that you’re only loading the correct subset, e.g. latin, and not the full font.
A tool like glyphhanger can be used to scan your site for a whitelist of all the characters that it uses and then create a subset from that.
Or you can create a general-purpose font file using fonttools that includes a pre-defined set of generic letters, numbers and symbols.
# Letters
AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
# Language-specific letters
ÕõÄäÖöÜüŠšŽž
# Numbers/math
<≤012²3³456789ⁿ∞°+-±=≠~≈%÷*×^∑∏≥>
# Punctuation
.,;:!?…
# Currency
$£€
# Symbols
#@&©®™
# Quotes
'"“”‘’‛„‟`
# Brackets
()[]{}
# Bullets
●•⦁·
# Arrows
→←«»↑↓➔➜↖↗↘↙
# Misc
|–—_/\§U+20,U+21,U+23-7E,U+A3,U+A7,U+A9,U+AB,U+AE,U+B0-B3,U+B7,U+BB,U+C4,U+D5-D7,U+DC,U+E4,U+F5-F7,U+FC,U+2013,U+2014,U+2018,U+2019,U+201B-201F,U+2022,U+2026,U+207F,U+20AC,U+2122,U+2190-2193,U+2196-2199,U+220F,U+2211,U+221E,U+2248,U+2260,U+2264,U+2265,U+25CF,U+2794,U+279C,U+2981You can check the original font file with a tool like FontDrop! for information about axes, instances, OpenType features, glyphs, ligatures and supported languages.
pyftsubset "VendSans.ttf" --output-file="VendSans-subset.woff2" --flavor=woff2 --layout-features+=liga,kern --retain-gids --no-hinting --unicodes=...With the font tools subsetting tool you can create a subset suitable for you.
Additional notes
Benefits to self-hosting a font
- Preloading – when using a Google font you can’t efficiently pre-load the font file because it’s loaded with a hashed name from the CSS that you include.
- Reduced latency – fonts load directly from your server, eliminating third-party delays.
- Caching control – we set custom cache headers to optimize how browsers store and reuse font files.
- No third-party tracking, GDPR compliance – reduces concerns about user data being sent to external servers, which is critical for privacy regulations.
Downsides to self-hosting a font
- Marginally slower – a CDN tailored for serving fonts can get the job done in 20-30ms, serving it yourself from a regular web server that makes additional checks may take 50-60ms. When it comes to Google fonts or similar setups, it’s still faster overall because you have a concrete font file URL you can pre-load with a high priority.
- Needs subsetting – if you download a font from Google Fonts it’ll be around
500kb, even though when loading it from Google Fonts you were only served~20-50kbchunks of it for the subset that was appropriate (e.g. latin).
So when self-hosting you’ll also want to subset the font in order to not serve a drastically bigger file with unnecessary characters.
Which font file format to use?
woff2 is the best modern font file format and you should prefer to use that. You can provide an alternative woff, otf or ttf as a fallback but the support for woff2 is good and it’s not the end of the world if a user with an old browser can’t load the font so it’s probably not worth the extra effort.
Child theme style.css on the front end
With ska-theme the child theme’s stylesheet is enqueued automatically but if you’re using some other theme you may need to enqueue it yourself via the child theme’s functions.php:
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style(
'your-child-theme',
get_theme_file_uri('style.css'),
[],
wp_get_theme()->get('Version')
);
}, 150);If you don’t want ska-theme to automatically load the style it can be turned off using:
add_filter('ska_theme_enqueue_child_theme_stylesheet', '__return_false');Child theme style.css in the block editor
With ska-theme the child theme’s stylesheet is automatically enqueued in the block editor. The above method for enqueueing the style on the front end should also carry over to the block editor, but if you need to enqueue it manually then you can add it to the block editor settings with the following filter:
add_filter('block_editor_settings_all', function($editor_settings) {
if(get_stylesheet() !== get_template()) {
$editor_settings['styles'][] = [
'id' => 'your-child-theme',
'css' => file_get_contents(get_theme_file_path('style.css')),
'__unstableType' => 'theme',
];
}
return $editor_settings;
});Child theme style.css in the classic editor
With ska-theme the child theme’s stylesheet is automatically enqueued in the TinyMCE editor. Should you need to do it yourself, here’s the code:
add_filter('mce_css', function($css = '') {
if(get_stylesheet() !== get_template()) {
return $css . ',' . get_theme_file_uri('style.css');
}
return $css; // Comma-delimited list of stylesheets.
});Access control for custom fonts
If you need serve your custom fonts on multiple domains the .htaccess file should include directives to provide cross-origin permissions:
# Serve fonts with an efficient cache policy
<IfModule mod_headers.c>
<FilesMatch "\.(otf|ttf|woff|woff2)$">
SetEnvIf Origin "https://(www\.)?klaviyo\.com$" ORIGIN_ALLOWED=$0
Header set Access-Control-Allow-Origin "%{ORIGIN_ALLOWED}e" env=ORIGIN_ALLOWED
Header set Cache-Control "max-age=315360000, public"
</FilesMatch>
</IfModule>



