Privacy & Security

How to host fonts locally instead of using Google Fonts

A practical, privacy-aware guide to downloading, subsetting, serving, and testing web fonts from your own domain.

The Wux Webtools Team The Wux Webtools Team 9 min read AI-assisted, human-reviewed
Illustration of locally hosted web font files being served from a website instead of a third-party service.
Table of contents
  1. Why self-host Google Fonts?
  2. What changes when you self-host
  3. Step 1: Audit what you actually use
  4. Step 2: Download the right font files
  5. Step 3: Subset fonts where appropriate
  6. Step 4: Write your `@font-face` rules
  7. Step 5: Remove the external Google Fonts calls
  8. Step 6: Set cache headers
  9. Step 7: Consider preloading only the critical font
  10. Step 8: Test privacy and performance
  11. Common mistakes to avoid
  12. Hosting too many weights
  13. Forgetting italics
  14. Keeping the old Google CSS link
  15. Serving fonts without long-term caching
  16. Ignoring legal and documentation work
  17. A simple migration checklist

Why self-host Google Fonts?

Google Fonts made good typography easy. Add a stylesheet, choose a few weights, ship the page. For years that was the sensible default for small teams.

The trade-off is that every visitor’s browser contacts a third-party service to fetch font CSS and font files. That has two consequences.

First, it adds an external dependency to rendering. If the font CSS is slow, blocked, or unavailable in a user’s region or network, your page waits or falls back.

Second, it creates a privacy question. A font request can reveal the user’s IP address, user agent, referrer policy context, and timing information to a third party. Google Fonts states that it does not set cookies through the Fonts API, but “no cookies” is not the same as “no personal data.” Under GDPR, an IP address can still be personal data in context.

Self-hosting fonts is not automatically required for every website, and this is not legal advice. But for European sites, public-sector sites, healthcare, education, finance, or any team trying to reduce unnecessary third-party requests, local hosting is usually the cleaner choice.

It is also often a performance win when done well. The catch is “done well.” Copying six font files into /assets/fonts/ and loading them all on every page can be worse than using the hosted service. If you want the broader performance context, our earlier piece on why web fonts are still the easiest performance win on most sites covers the common waste patterns.

What changes when you self-host

When you use Google Fonts the usual way, your page does this:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">

The browser first requests CSS from fonts.googleapis.com, then downloads font files from fonts.gstatic.com.

When you self-host, your page should request both the CSS and the font files from your own domain:

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

That removes the third-party font request. It also makes you responsible for choosing file formats, cache headers, fallback fonts, and updates.

That responsibility is worth taking seriously. Fonts sit on the critical rendering path. A poor font setup can cause invisible text, layout shifts, and slow first render.

Step 1: Audit what you actually use

Before downloading anything, list the font families, weights, styles, and character sets your site truly needs.

A typical marketing site might need:

  • Regular 400 for body text
  • Semibold 600 or bold 700 for headings and buttons
  • Italic 400 only if the design actually uses italics
  • Latin character set only, unless the site supports more languages

Be suspicious of old design-system defaults. Many sites load 300, 400, 500, 600, 700, italics, and multiple scripts because somebody selected them once in a font picker.

In browser DevTools, open the Network panel, filter by “font,” reload the page, and check which files are requested. Then inspect your CSS for font-weight usage. If your CSS never uses 300, do not host 300.

If you are reviewing the impact later, Lighthouse can help, but do not treat its score as the whole story. Use it as a diagnostic tool, not a judge. We have a separate guide on reading a Lighthouse report without panicking that is useful when prioritising font fixes.

Step 2: Download the right font files

Google Fonts offers open-source fonts. You can download them from the Google Fonts website or from the relevant font project repository. Check the license, but most Google Fonts are distributed under open licenses such as the SIL Open Font License or Apache License.

For the web, prefer WOFF2. It is widely supported by modern browsers and usually much smaller than TTF or OTF. In 2026, serving TTF directly to browsers is rarely justified for public websites.

A sensible directory structure looks like this:

/public
  /fonts
    inter-latin-400.woff2
    inter-latin-600.woff2
    inter-latin-700.woff2

Use descriptive filenames. Six months later, font.woff2 will be annoying. inter-latin-600.woff2 is boring and useful.

If your site uses a build system, keep the source fonts somewhere clear and let the build pipeline copy optimized files into the public assets directory.

Step 3: Subset fonts where appropriate

Subsetting means removing characters you do not need. A full font can include Latin, Cyrillic, Greek, Vietnamese, symbols, and many OpenType features. If your English-only landing page needs only Latin characters, a subset can be dramatically smaller.

There are two common approaches:

  1. Use a prebuilt subset from the font provider or repository.
  2. Generate your own subset with a font tool such as pyftsubset from fonttools.

For many teams, prebuilt Latin subsets are enough. Custom subsetting is useful when you have very constrained pages, such as a single campaign page with limited text, or a product UI with predictable character coverage.

Be careful with multilingual sites. Missing glyphs cause fallback font mixing, which can look broken and harm readability. If you support multiple languages, map font subsets to language routes rather than forcing one tiny subset everywhere.

Step 4: Write your @font-face rules

A minimal local setup looks like this:

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-latin-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

A few details matter here.

Use font-display: swap for most content sites. It tells the browser to show fallback text quickly, then swap in the web font when it arrives. That avoids the worst version of FOIT: flash of invisible text.

Set an explicit fallback stack. If the custom font fails, users should still get readable text. Fallbacks are not an afterthought; they are part of the design. If you need to revisit sizing, line length, and body text choices, start with a practical guide to readable type on the modern web.

Match weights correctly. If your CSS asks for font-weight: 500 but you only define 400 and 700, the browser may synthesize an intermediate weight. That is not always terrible, but it can look inconsistent.

Step 5: Remove the external Google Fonts calls

After adding local font CSS, remove the old remote calls from your templates.

Look for:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?..." rel="stylesheet">

Also check:

  • Theme settings in CMS platforms
  • Page-builder typography panels
  • Third-party widgets
  • Tag managers
  • Old CSS imports such as @import url('https://fonts.googleapis.com/...')

That last one is common. CSS @import for fonts is usually worse for performance because it delays discovery. If you self-host, define fonts directly in your main CSS or a font CSS file loaded early.

Privacy work often fails because teams fix the obvious template but miss scripts, widgets, and legacy embeds. That same pattern shows up in consent work; our guide to what changed for cookies in 2026 is a useful companion if you are reducing third-party surface area more broadly.

Step 6: Set cache headers

Font files are static assets. They should be cached aggressively if their filenames are versioned or content-hashed.

A good production header is:

Cache-Control: public, max-age=31536000, immutable

Only use long-lived immutable caching if the URL changes when the file changes. For example:

inter-latin-400.a8f3c2.woff2

or a versioned path:

/fonts/v2/inter-latin-400.woff2

If you overwrite /fonts/inter-latin-400.woff2 without changing the URL, some users may keep the old file for a long time. That is fine until it is not. Versioning avoids the problem.

Also serve fonts with the correct MIME type:

Content-Type: font/woff2

Most modern hosting platforms handle this automatically, but it is worth verifying.

Step 7: Consider preloading only the critical font

Preloading can help the browser discover an important font earlier:

<link rel="preload" href="/fonts/inter-latin-400.woff2" as="font" type="font/woff2" crossorigin>

Use this sparingly. Preload the primary above-the-fold text font, not every font weight. Over-preloading competes with CSS, images, and JavaScript.

Even for same-origin fonts, include crossorigin on font preloads. Font fetching uses CORS mode, and omitting it can cause duplicate downloads in some setups.

If you are unsure, test. Do not cargo-cult preloads because a checklist said so.

Step 8: Test privacy and performance

Testing is straightforward.

Open DevTools, reload the page with cache disabled, and filter the Network panel for:

  • fonts.googleapis.com
  • fonts.gstatic.com
  • .woff2
  • font

You should see font files served from your own domain and no Google Fonts requests.

Then test with a cold cache and a warm cache. On the first visit, fonts should download once. On later visits, they should come from memory or disk cache depending on the browser.

Check for layout shift as the font swaps in. If headings jump, your fallback font metrics differ too much from the web font. You can reduce the visible shift by choosing a closer fallback or using newer CSS font metric overrides such as size-adjust, ascent-override, descent-override, and line-gap-override. These are more advanced, but useful for polished interfaces.

Finally, test pages in private browsing or with content blockers enabled. One benefit of self-hosting is that privacy tools are less likely to block your typography accidentally.

Common mistakes to avoid

Hosting too many weights

This is the most common failure. Two weights are often enough. Three is usually plenty. Five is a design-system smell unless you have a strong reason.

Forgetting italics

If your content uses real emphasis, load a real italic file. Synthetic italics can look poor, especially in long-form editorial content.

This defeats the point. After migration, no font request should go to Google unless another component is injecting it.

Serving fonts without long-term caching

Self-hosting gives you control. Use it. Fonts are ideal candidates for long cache lifetimes.

If your privacy policy previously mentioned Google Fonts or third-party font loading, update it after migration. If you maintain a data-processing inventory, update that too. The technical change and the compliance record should agree.

<!-- tool-cta:start -->

💡 Try this: Convert the TTF files you downloaded from Google Fonts into self-hostable WOFF2 plus CSS with the Webfont Generator.

<!-- tool-cta:end -->

A simple migration checklist

  1. List the font families, weights, styles, and scripts you actually use.
  2. Download WOFF2 files and confirm the license.
  3. Subset fonts if the site has limited language needs.
  4. Add local @font-face rules with font-display: swap.
  5. Remove all Google Fonts link, preconnect, and @import references.
  6. Serve fonts from your own domain with long-lived cache headers.
  7. Preload only the most important above-the-fold font, if testing supports it.
  8. Verify in DevTools that no Google Fonts requests remain.
  9. Update privacy documentation if needed.

Self-hosting fonts is not glamorous work. It is the kind of small infrastructure cleanup that reduces dependency risk, improves privacy posture, and gives you more predictable rendering. That is usually worth the hour or two it takes.

Frequently asked questions

Is it legal to self-host Google Fonts?
Usually, yes. Most fonts available through Google Fonts are open-source and can be self-hosted under their respective licenses. Always check the specific font license before shipping it.
Does self-hosting fonts automatically make my site GDPR compliant?
No. It only removes one common third-party data transfer. GDPR compliance depends on your broader data collection, consent, documentation, and vendor setup. But self-hosting fonts is a practical privacy improvement.
Should I use WOFF2 only?
For most modern websites, yes. WOFF2 has broad browser support and strong compression. Legacy formats such as TTF, OTF, EOT, and SVG fonts are rarely needed now.
Will local fonts always be faster than Google Fonts?
Not always. Poorly hosted local fonts can be slower. Local hosting works best when you use small WOFF2 files, avoid unnecessary weights, set proper cache headers, and serve fonts from fast infrastructure.
How do I know if Google Fonts is still loading?
Open browser DevTools, reload the page, and check the Network panel for requests to `fonts.googleapis.com` or `fonts.gstatic.com`. Also search your templates and CSS for old Google Fonts links or `@import` rules.

Sources & further reading

  1. MDN Web Docs: @font-face
  2. web.dev: Optimize webfont loading and rendering
  3. Google Fonts FAQ
  4. Regulation (EU) 2016/679: General Data Protection Regulation
About the author
The Wux Webtools Team

Last updated:

Keep reading