Self-Hosting Web Font Files

September 27, 2023 | 10 minutes

Web fonts have greatly improved typography on the web. Before them, we were limited to a handful of web-safe fonts to ensure that a website's text would display as intended across devices. While we can still use web-safe fonts and often do so as fallback options, we now have the flexibility to use custom web fonts. With custom web fonts, a user's device will download the font files when they visit your website.

TL;DR: You can host your font files alongside your other static website files (CSS, images, etc.) and load them with CSS @font-face rules.

Web Fonts and Performance

Before diving into self-hosting, I think it's important to note that the most performant approach to hosting web fonts is usually using the font repository's CDN. Font repositories like Google Fonts heavily optimize their font delivery logic to provide users with the smallest font files possible given their browser's capabilities. This can benefit your users without you needing to implement the logic on your own.

Quick delivery of fonts is important because the page can't be properly displayed until the fonts have been downloaded. The time it takes to download the fonts will impact how quickly the website is displayed and may cause a flash of unstyled content (sometimes referred to as FOUC) if the font files take too long to download.

An example of a flash of unstyled content. The text first loads with the fallback system font for `sans-serif` and then swaps in the Dancing Script font once it's ready.

If performance is your primary focus and you're using Google Fonts, I'd recommend checking out Sia Karamalegos' article Making Google Fonts Faster in 2022 as it has many great tips on optimizing your use of the Google Fonts CDN.

Reasons for Self-Hosting Fonts

While performance is important, there are times when you need to balance performance with other goals. If you're working on a website with traffic from users in the European Union (EU), for example, it may be best to host your own web fonts because Google Fonts collects user data and therefore doesn't meet the EU's General Data Protection Regulation (GDPR) requirements. Or, maybe you're working on a web application that can be used offline and need more granular control over how the font files are cached. In these situations, self-hosting your web fonts may be the best approach.

Given the title of this article, I'm assuming you're here to learn how to self-host your font files, so let's look at what's needed to do so!

How to Self-Host Your Web Fonts

Hosting your font files requires downloading the files for your font from the font repository, storing them in a publicly accessible folder on your server, and referencing them with CSS @font-face rules.

Note: If you're using Next.js with Google Fonts, you can use the next/font module to easily configure self-hosted Google Fonts. Other frameworks may have similar options available, so it's worth checking before following the steps below.

Let's walk through the process of self-hosting a Google Font. As an example, I'll use the Dancing Script font. If you're using fonts from a different repository, start with step 2.

  1. Toward the top of the font page, click the "Download family" button. This will download a .zip folder to your machine with all of the font files as .ttf (TrueType) files for the selected font.

    Google Fonts only provides TrueType font files for download. TrueType fonts are widely supported across browsers and will work fine on their own, but they aren't compressed like WOFF and WOFF2 files and will take longer to download. To further reduce font download times, you can use the google-webfonts-helper to get different font file types for your selected Google Fonts. For the example, I've used google-webfonts-helper to download a few of the WOFF2 files for Dancing Script to use as an example of how to configure multiple file types for a font.

    A screenshot of the downloaded Dancing Script Web Font Files from Google Fonts and the google-webfonts-helper
  2. Copy the .ttf files (and any other font file formats) into a folder on your website. If you're using a framework like Gatsby.js, you'll need to treat the font files like you would other static files and may also need to import the font files in your JavaScript.

  3. At the top of your CSS file, add an @font-face rule as follows. Be sure that the src property references the location of your font files.

    1@font-face {
    2  font-family: "Dancing Script";
    3  font-style: normal;
    4  font-weight: 400;
    5  src: 
    6     url(/fonts/dancing-script-v25-latin-regular.woff2) format("woff2"),
    7     url(/fonts/DancingScript-Regular.ttf) format("truetype");

    When the browser needs a font, it will use the first source it can successfully load; if it doesn't support a format, it will skip that source and try to load the next one. So, putting the smallest format first (like above) is generally best for performance.

  4. Repeat the CSS above for each font weight and style that you need.

    1@font-face {
    2   font-family: "Dancing Script";
    3   font-style: normal;
    4   font-weight: 700;
    5   src: url(/fonts/DancingScript-Bold.ttf) format("truetype");
  5. Now, you can use your web fonts by referring to them as you would any other font. The browser will know which font file to use based on the combination of font family, weight, and style you use.

    1body {
    2 font-family: "Dancing Script", cursive;
    3 font-weight: 400;
    4 text-align: center;

The fonts are only loaded when they're referenced using the font-family property for a CSS rule that's applied to an element on the page. If none of the elements on the page use the web font, then it won't be loaded.

For more complex fonts, there are additional font properties you can set using the @font-face rule.

Complete Example

Here's a CodeSandbox with a few of the Dancing Script fonts configured:

Testing Your Font Setup

Now that we've set up our web font, we should verify that the browser is actually using it when rendering the web page. We can do this using the Chrome dev tools. The technique below will work on localhost and your live website.

  1. In Chrome, go to a webpage where you're using the web font.
  2. Right-click on a piece of text that's using the web font and select "Inspect" from the context menu.
  3. The dev tools should open with the "Elements" tab visible. On the right side, select the "Computed" tab.
  4. Scroll to the bottom and you should see a "Rendered Fonts" section. If it's using your web font, it should show the name of the web font below the "Rendered Fonts" headline.
Using Chrome Dev Tools to check that text is rendered with the web font instead of the fallback font

Testing Your Fallback Font

The browser will download the web fonts when it loads your website, so in many cases, users will see the website's text and design as intended. As with all things in tech, though, not everyone experiences the "happy path." For example, the browser may use a fallback option instead of the web font when:

  • The web font's hosting service is down
  • It takes too long to download the web font
  • The user is offline
  • (Possible future scenario) The user has set prefers-reduced-data to true through their system settings*

Since some users may see the site without the web fonts, it's important to test and ensure that your design appears as intended with the fallback fonts as well. You can test your design with the fallback font by building on the example above.

  1. Change tabs from "Computed" to "Styles" in the dev tools.
  2. Find the font-family for the selected text element and remove your web font from the list.
  3. You should notice the text change on the site and if you go back to the "Computed" tab, under "Rendered Fonts" it should now show either your next fallback font (if you specified a specific font) or the system's default font (if you specific a generic font, like sans-serif).
Using Chrome Dev Tools to review a design using fallback fonts
*The prefers-reduced-data CSS media query was proposed a few years ago, but it hasn't been implemented anywhere yet. If it's implemented in the future, one way to respect the user's preference for less data would be to not load web fonts.

Improving Performance When Self-Hosting Fonts

While self-hosting the files may not always be the most performant approach, there are performance optimizations you can make.

Use WOFF2 Files When Possible

The WOFF2 file format offers a 30% improvement in compression over WOFF, provides the same metadata as TTF files, and is widely supported by modern browsers. Also, while Google Fonts only provides .ttf files for direct download, there are ways to get the .woff2 files as explained above.

If you have multiple file formats for a font, be sure to set the smallest file format as the first src option in the @font-face rule in your CSS. Browsers will load the first src that they can successfully parse.

Load a Subset of Characters

Fonts generally ship with characters to cover many different needs, but you may not use all of them on your website. If you know you only need a subset of characters from the font, use the unicode-range property in the @font-face rule to specify the range of characters to load and make available for use on a page.

In the example below, the unicode-range tells the browser to load only the Latin characters for the font.

1@font-face {
2  font-family: "Dancing Script";
3  font-style: normal;
4  font-weight: 700;
5  src: url(/fonts/DancingScript-Bold.ttf) format("truetype");
6  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;

Loading a subset of the characters will be faster than loading all of them. If you don't use any of the characters in the unicode-range on the page, then the browser won't load the font at all.

Set the font-display Property

The font-display CSS property tells the browser how to display the font based on whether and when it's downloaded and ready. The default value is auto, which will do what the browser's user agent prefers. The block, swap, and fallback options let you tell the browser how you'd prefer to handle displaying web fonts. Each option blocks the rendering of the page for some time while waiting for the web font to download but to different degrees. The swap option gives you the best chance of your web font being rendered, but it can cause a flash of unstyled content depending on how long it takes to load the font. The fallback option reduces the amount of time available to swap the fonts (reducing layout shifts after the page has loaded) but makes it more likely that the web font isn't rendered. Try the different options and choose the one that best meets your needs.

1@font-face {
2  font-family: "Dancing Script";
3  font-style: normal;
4  font-weight: 400;
5  src: url(/fonts/dancing-script-v25-latin-regular.woff2) format("woff2"),
6    url(/fonts/DancingScript-Regular.ttf) format("truetype");
8  /* Tells the browser whether and how long to wait for web fonts to
9     load before showing the fallback font
11  */
12  font-display: swap;

Setting the size-adjust Property

The size-adjust property specifies a percent value to use for glyph outlines and other font metrics. It helps reduce layout shifts when swapping between the fallback font and a web font. The percent value should be the value needed to scale the ex height of the web font glyphs to the ex height of the fallback font. Setting the size-adjust property won't improve font performance per se, but it will improve perceived performance by making the font swap less noticeable.

1@font-face {
2  font-family: "Dancing Script";
3  font-style: normal;
4  font-weight: 400;
5  src: url(/fonts/dancing-script-v25-latin-regular.woff2) format("woff2"),
6    url(/fonts/DancingScript-Regular.ttf) format("truetype");
7  size-adjust: 90%;
Thumbnail photo by Markus Spiske from Pexels