Improving website performance by loading fonts the smart way


My site is using a system font stack, mostly because I would like to have the lightest website possible.

If I want to be listed in the, every kB added to the website matters.

My featured images, heavily optimised, still account for large LCP (Largest Contentful Paint) and adding custom fonts can increase the overall weight of the website, way above 512kB.

I am happy with my site, but on others, I need to use specified fonts to get the right visual experience across all user devices. In that case, I need to load additional fonts to accomplish that.

The fonts not only add weight to the website but also can have a negative impact on CLS (Cumulative Layout Shift) if loaded incorrectly. As CLS is part of essential Core Web Vitals, its poor score can drop down a website significantly in Google Search results.

Ironically, Google, on their Google Fonts website provides information (code) on how to implement the desired font in our website. The problem is, that their solution will have a negative impact on your website.

Not only they are loaded from an external source, but the speed of loading of your website and external fonts can also vary and cause CLS.

A widely recommended method is to self-host your fonts (even these from Google). This, in most cases, may improve an impact on CLS, however not always. There are plenty of factors on how these fonts are delivered. If our hosting is poor and we are not using CDN (Content Delivery Network) then we can see worse results than loading fonts directly as Google advised.

Self-hosting fonts are the right approach, but it requires a couple of tweaks before it will work well for us in matters of web performance.

Let’s start with, how to load them correctly.

Self-Hosted fonts #

Your website will require a font in WOFF format (The Web Open Font Format). This is the format that is practically used by all browsers currently on the market.

A hint! There are two versions of WOFF formats. WOFF and WOFF2. The difference between them is the compression algorithm. WOFF2 files are smaller, hence will load quicker.

It is recommended to use WOFF2 by default, but also have WOFF files available as a backup for older browsers.

If you fancy Google Fonts then you will be happy to head to google webfonts helper to find your desired font and download required files, along with the code, showing you how to implement them on your website.

This website is a real gold nugget. We will use files provided by them, but will not use their code, at least not in full. Additionally, we will do some tweaks to work better for us.

Let’s start with the most popular serif font ‌Merriweather.

By default, we will select charsets latin (default) and styles as it suits us.

In third point we will get our css code to copy. Before we copy it, we will customize folder prefix as it suite us (in my example its just /fonts/.

/* merriweather-regular - latin */
@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 400;
  src: url('/fonts/merriweather-v28-latin-regular.eot'); /* IE9 Compat Modes */
  src: local(''),
       url('/fonts/merriweather-v28-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
       url('/fonts/merriweather-v28-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
       url('/fonts/merriweather-v28-latin-regular.woff') format('woff'), /* Modern Browsers */
       url('/fonts/merriweather-v28-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
       url('/fonts/merriweather-v28-latin-regular.svg#Merriweather') format('svg'); /* Legacy iOS */

The last point will allow us to download the required files.

Once downloaded and unpacked we will have the following:


The Tweaks #

Before we will go a little further, lets see whats the important difference between this code, and one provided from Google Fonts for the same font.

@import url('');

When you put Google Fonts code into your style.css it will import the following (showing only latin part):

/* latin */
@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url( format('woff2');
  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;

Google is importing all other charsets that you may not necessarily need!

If you will use this code before your font will load and swap the style on your website (font-display: swap;), the text will be presented with the locally available font (can be split second).

Depending on your internet speed, the time between displaying local alternative font (Merriweather is not available by default in any operating system) and Merriweather will likely cause a move on your website. This move will account for CLS and have a negative impact on your website.

What code from google webfonts helper did to reduce this impact, is to tell the browser, that there are no local fonts available at all, so the text needs to be displayed once the font is loaded and available.

This is the part of the code that helps us to deal with unwanted CLS when using custom fonts on our website.

src: local(''), url('/fonts/merriweather-v28-latin-regular.woff2')

By looking into Google Fonts code, this small but really important hack doing a big difference in Core Web Vital (especially CLS).

Sometimes I feel, that Google is presenting their services that work opposite to what they preach. Google Fonts, Google Ads… they simply killing your website CLS metric (a Googles’ PageSpeed metric) when used as provided by… Google (crazy!).

The code from the other website is missing however something, that Google is implementing in their code

font-display: swap;

“The font-display descriptor determines how a font face is displayed based on whether and when it is downloaded and ready to use.”

Despite that we are telling that there are no local fonts available (local('')), in reality, they are, and when they need to be used, they shall be swapped as soon as possible with a loaded font.

Let’s add that to our code.

From both codes above, there is missing one option that I discovered some time ago – text-rendering.

“The text-rendering CSS property provides information to the rendering engine about what to optimize for when rendering text.”

From the couple options available, I recommend optimizeLegibility.

“The browser emphasizes legibility over rendering speed and geometric precision. This enables kerning and optional ligatures.”

Some fonts may look nice but can be hard to read when letters are blended.

Here is an example from of how this will work.

optimizeLegibility Example

Legibility means “the quality of being clear enough to read”.

This parameter provides clarity of the text making reading of custom fonts easier for us, the actual users.

Our additional code will look as follow:

text-rendering: optimizeLegibility;

The Final Code #

We have added src: local(''), font-display: swap; and text-rendering: optimizeLegibility;. Now it is time to do a cleanup.

Apart of WOFF and WOFF2, we don’t need other fonts in our /font/ folder, so lets remove it.

Also, WOFF2 is recommended for use in first place, hence we will change places (order) with WOFF, to be users first.

This is how this will look on the end.

/* merriweather-regular - latin */
@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local(''),
       url('/fonts/merriweather-v28-latin-regular.woff2') format('woff2'),
       url('/fonts/merriweather-v28-latin-regular.woff') format('woff');
  text-rendering: optimizeLegibility;

This code we shall put as a first part into our style.css, so this will start loading before other elements start getting styling in the further part of this file.

We need to remember, to load out style.css from the <head> as quick as possible.

<!DOCTYPE html>
		<link rel="stylesheet" type="text/css" href="style.css" media="all">

If we follow all as mentioned above, we shall see huge improvement when running our website through PageSpeed Insight, especially CSL.

That’s however depend, how fast is our hosting and if we using CDN or not.

PageSpeed Metrics example