How to self-host Google fonts for your web application | Part 2/2: Download and provision
Tags: privacy, webtech
Published: 2021-09-15 15:11:29

In the [first part](https://blog.q-bit.me/how-to-self-host-google-fonts-part-one) of this series, I've attempted to compare self-hosting Google fonts with having them provided by Google. My personal winner was the former option and this article describes how to implement it.

Recapitulation: DIY or CDN?
---------------------------

In a nutshell: Should you host your own fonts?

*   Are you serving your website to European citizens and don't want all that additional GDPR hassle (user consent, an extensive privacy policy, legal notes, ... you name it)?
*   Do you want more control over your served content? And do you feel confident to adjust some configuration on your server?
*   Would you like to rely less on the big player's infrastructure to serve critical files? Or are you tired of consultants and lawyers telling you to do so?

If you find yourself among these three, you are likely a good candidate.

Download the fonts you would like to self-host
----------------------------------------------

For the following steps, I'll assume you have the following project structure.

```bash
/
| - fonts/
| - styles/
|   | - main.css
| - index.html
```

*   Head over to [https://fonts.google.com/](https://fonts.google.com/) and select your favorite font(s)
*   Click on 'Download family' and confirm the download

![](https://blog.q-bit.me/content/images/2021/09/image-3.png)

*   Save the downloaded file in your project folder
*   Unzip the directory.

You will now have a variety of `.ttf` files available. Each of these files represents a specific font style

![](https://blog.q-bit.me/content/images/2021/09/image-4.png)

> You can achieve even better results using [Google fonts helper](https://google-webfonts-helper.herokuapp.com/fonts). It provides you more modern formats with fallback options & CSS snippets.

Add the font-face rules to your CSS
-----------------------------------

Now that we have the fonts available, we must configure them in our styles. This is done by using the [@font-face rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face).

```css
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 500;
  src: url('../fonts/Roboto-Regular.ttf');
  font-display: swap;
}
```

You can then start applying the `font-family` as you usually would:

```css
.custom-font {
  font-family: 'Roboto', 'sans-serif'
}
```

Next, apply the class `custom-font` to your HTML. Notice how the style, followed by the font file, is then loaded into the browser:

![](https://blog.q-bit.me/content/images/2021/09/image-5.png)

So far so good, but there are still some improvements to be made here. Google still does some important things for us, some of which are:

*   Providing browser-specific font types
*   Taking care of preloading fonts without blocking the browser
*   Setting the correct headers for cache control

We can, however, set these features up ourselves, so let's go ahead and do that

Get the browser-specific - version of your font
-----------------------------------------------

Google's CDN automatically serves you the font format that is supported by your browser. And while the `.ttf` file from earlier works, `.woff` formatted fonts are served faster and [supported by all modern browsers](https://caniuse.com/woff).  

> Using the [Google fonts helper](https://google-webfonts-helper.herokuapp.com/fonts) app saves you a bit of manual labor here.

Fortunately, there's a simple trick to figure the correct font to download. Let's take **Roboto** as an example again.

*   Head to the browser you require the fonts for (or use a browser simulator like [Browserling](https://www.browserling.com/))
*   Parse the URL you found on Google fonts and navigate to it

![](https://blog.q-bit.me/content/images/2021/09/image-7.png)

*   You will find a CSS file that has the optimal `@font-face` CSS rules for your browser

![](https://blog.q-bit.me/content/images/2021/09/image-9.png)

Some examples for other font formats and browsers using them are

*   **Chromium-based Browsers & Firefox** | Font Type `.woff2`  
    [https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu72xKOzY.woff2](https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu72xKOzY.woff2)
*   **Internet Explorer 10/11** | Font Type `.woff`  
    [https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Me5g.woff](https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Me5g.woff)
*   **Safari 4** | Font Type `.tff`  
    [https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Me5Q.tt](https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Me5Q.ttf)

After downloading the font and replacing the source URL in the `main.css` file, everything still looks as expected:

![](https://blog.q-bit.me/content/images/2021/09/image-10.png)

Configure your app to preload fonts
-----------------------------------

By default, loading CSS happens synchronously and thereby [blocks the browser's rendering process](https://developer.mozilla.org/en-US/docs/Web/Performance/Critical_rendering_path). This also includes our `@font-face` CSS rules. Before rendering text, the browser must first load the necessary fonts. This takes time and is bothersome for users. Luckily, it is also fairly simple to circumvent.

> With this approach, textual content is rendered before the fonts are available. Then, once loaded, they're applied - the fonts are swapped.

In your `index.html`:

*   Instead of requesting the CSS file that deals with fonts with `rel="stylesheet"`, add the `rel="preload"` and `as=font` attributes
*   Then, use the `window.onload` event to overwrite the `rel` attribute once the HTML is done loading

```html
<!-- Replace -->
<link rel="stylesheet" href="./styles/main.css">

<!-- With -->
<link rel="preload" as="font" href="./styles/main.css" onload="this.onload=null;this.rel='stylesheet'">
```

> This trick alone boosted my lighthouse score by an unbelievable amount of 8%

Configure your server to send a cache header
--------------------------------------------

To demonstrate this, I use Nginx v1.18 on a 5$ Digital Ocean Droplet:

*   SSH into your droplet with `ssh root@<ip-adress>`
*   Open the default Nginx server config `nano /etc/nginx/sites-endabled/default/`
*   Add the following wildcard rule right above the first `location` to apply a cache of 30 days _(=2592000 seconds)_ for static assets

```bash
# Set cache headers for all css and font files
# This header can also be extended for other static content
location ~* \.(css|ttf|woff|woff2) {
  add_header Pragma public;
  add_header Cache-control "public max-age=2592000";
}

# location / { ... }
```

*   Use `systemctl reload nginx` to gracefully restart Nginx
*   Move your files to the droplet using Linux' `scp` or, alternatively, [Filezilla](https://filezilla-project.org/)

```bash
# Copy the whole directory to the droplet 
# Note that I am using /test/ as a path instead of just / 
# as scp recursively copies the whole folder structure
scp -r <path-to-project>/test/ root@<droplet-ip>:/var/www/html/
```

Once that is done, try and access your droplet by its IP address. You should see something like this:

![](https://blog.q-bit.me/content/images/2021/09/image-11.png)

If you made it here in one piece (especially if you've read the bit about data privacy), you're now the proud owner of a website with its own instance of Google Fonts.

Further reading & resources
---------------------------

There are more excellent projects and articles on the internet that deal with this topic.

*   Using a web framework? Use 'Fontsource' to install fonts with npm:  
    [https://fontsource.org/docs/introduction](https://fontsource.org/docs/introduction)
*   The cost of moving from Google Fonts to self-hosted web fonts:  
    [https://jeremenichelli.io/2020/07/google-fonts-self-host-web-fonts/](https://jeremenichelli.io/2020/07/google-fonts-self-host-web-fonts/)
*   A list of popular Google Fonts pairings:  
    [https://www.w3schools.com/howto/howto\_google\_fonts\_pairings.asp](https://www.w3schools.com/howto/howto_google_fonts_pairings.asp)