Back to MakeFont

Guide

How to self-host web fonts instead of loading them from Google Fonts

Serve web fonts from your own server instead of Google Fonts: get WOFF2 files, declare them with @font-face, preload, cache, and set them up in Tailwind and Next.js. With a prompt for your agent.

9 min read · Updated 30 September 2026

Headline set in MakeFont Sans 01, a typeface made with MakeFont.

fonts.googleapis.com your-site.com/fonts Aa400 Aa700 OFL.txt Aa

Sites made with an AI tool usually pull their fonts from Google Fonts. Many templates do this out of the box, and one <link> tag is all it takes. The tradeoff is that each visitor's browser talks to Google before your page renders properly. If your site has visitors in the EU, that has carried legal risk since 2022, and avoiding it is straightforward: serve the font files from your own server. This guide walks through how to do that for plain HTML, Tailwind and Next.js, and how to verify that nothing reaches out to Google anymore.

Why it matters

Privacy and law. A page that fetches a font from fonts.googleapis.com makes the visitor's browser hand its IP address to Google. In January 2022, the Regional Court of Munich (LG München I, judgment of 20 January 2022, case 3 O 17493/20) decided that doing this without consent breaks the GDPR, and it granted the visitor 100 euros in damages. Site owners then received a wave of warning letters. This guide is not legal advice, but the technically safe route is simple: when the font is served by your own server, nothing is sent to Google.

Speed and control. Fonts you host yourself come from the same origin as the page, so the browser skips opening a connection to a separate server. You control how the files get cached, which characters they include and when they change. No one can swap out or delete a font underneath you.

Step 1: Get the font files

The format you want is WOFF2. It is compressed, every current browser supports it, and it is the only format the web needs.

  • From Google Fonts: find the font on fonts.google.com and download the family. That gives you TTF files, and often a variable font too. Turn them into WOFF2 with a tool like fonttools (pip install fonttools brotli, then fonttools ttLib.woff2 compress MyFont.ttf).
  • As an npm package: Fontsource ships most open-source fonts as npm packages, for instance npm install @fontsource/inter. Each package holds the WOFF2 files plus the CSS, and your build folds them into your site.
  • Your own font: when a font was made for your project, you most likely have WOFF2 files already. Treat them the same way.

Look at the license before hosting any font. Nearly every font on Google Fonts is under the SIL Open Font License or the Apache License 2.0, and both permit self-hosting. Store the license file alongside the font files.

Step 2: Put the files on your server

Drop the WOFF2 files into a directory your site exposes as static files, such as public/fonts/ in most frameworks, or fonts/ beside your HTML. Pick file names without spaces, and put the weight in the name when you have more than one file, like inter-400.woff2 and inter-700.woff2.

Step 3: Declare the font in CSS

Point the browser at the files with @font-face, then reference the family name the way you always would:

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

body {
  font-family: "Inter", system-ui, sans-serif;
}

With a variable font, a single file spans every weight. Pass the range rather than one value: font-weight: 100 900;.

font-display: swap renders text in a fallback font immediately and switches to your font once it arrives, so visitors never stare at a blank page.

After that, delete every <link> pointing at fonts.googleapis.com and every Google Fonts @import from your HTML and CSS. If you keep both, the font loads twice and data still flows to Google.

Step 4: Preload the font you see first

A browser only learns about a font once it hits the CSS rule that uses it. For the font behind your main headline or body copy, you can tell it sooner:

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

Fonts need the crossorigin attribute even when the file sits on your own domain. Leave it out and the browser fetches the file twice. Preload one or two files at most; preloading all of them makes the page slower.

Step 5: Cache the files for a long time

Font files almost never change. Send them with a long cache lifetime, such as Cache-Control: public, max-age=31536000, immutable. When you swap in a different font later, rename the new file so browsers pick it up. On Vercel, set the header for the fonts folder in vercel.json; on Netlify, use a _headers file.

Variable fonts in two minutes

Think of a variable font as one file covering a full spectrum of weights, and sometimes widths or other styles too. Rather than shipping inter-400.woff2, inter-600.woff2 and inter-700.woff2, you ship a single file and choose any weight within that spectrum.

Register it with a range like in step 3, then pick whatever value you need inside that range:

h1 { font-weight: 650; }

A single variable file usually weighs less than three or four static files combined, though more than one static file on its own. When your site sticks to just one weight, the static file is the lighter choice. Variable fonts work in every current browser.

In Tailwind

Set up the font with @font-face in your global CSS exactly as in step 3, then register the family name. In Tailwind CSS v4:

@theme {
  --font-sans: "Inter", system-ui, sans-serif;
}

With Tailwind v3, set theme.extend.fontFamily.sans in tailwind.config.js to ["Inter", "system-ui", "sans-serif"]. Utilities like font-sans will then render with your self-hosted file.

In Next.js

next/font takes care of self-hosting. When you import a Google font through next/font/google, Next.js fetches the files at build time and serves them from your own domain, so the visitor's browser never touches Google. For files of your own, reach for next/font/local:

import localFont from "next/font/local";

const inter = localFont({
  src: [
    { path: "./fonts/inter-400.woff2", weight: "400", style: "normal" },
    { path: "./fonts/inter-700.woff2", weight: "700", style: "normal" },
  ],
  display: "swap",
});

Put inter.className on your <html> or <body> element.

Avoid the jump when the font arrives

font-display: swap shows your text in a fallback font first. Once the real font lands, the glyphs resize a little and the layout may shift. You can bring the fallback close to your font's proportions by adding a second @font-face rule that tweaks a local system font:

@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107%; /* example value, depends on the font */
}

body {
  font-family: "Inter", "Inter Fallback", sans-serif;
}

Which percentage is right varies from font to font, so experiment until the swap is hardly noticeable. With next/font, this fallback gets calculated for you automatically.

WordPress, Webflow and other site builders

On these platforms the fonts typically come bundled with the theme or the builder itself rather than from code you wrote.

  • WordPress: plenty of themes pull Google Fonts by themselves. The Site Editor's Font Library stores Google Fonts on your own site rather than fetching them from Google. If you run an older theme, plugins like OMGF point the links at local copies.
  • Webflow: anything added via the Google Fonts setting gets served from Google. Instead, upload the font files as custom fonts in the site settings.
  • Lovable, v0, Bolt and similar tools: since they output code, this whole guide applies to them. The fastest route is the agent prompt below.

Once you have switched, do the check at the end of this guide. Plugins, embeds and leftover theme files are where a Google Fonts request typically hides.

Let your agent do it

Working with Claude Code, Cursor, Lovable, v0 or a comparable tool? You can delegate the entire task to your agent. For example:

Prompt for your agent

Self-host all fonts in this project. Download the font files we use as WOFF2 into the public fonts folder, declare them with @font-face and font-display: swap, preload the main text font, and remove every link or import that loads fonts from fonts.googleapis.com or fonts.gstatic.com. Keep the license files. Afterwards, list every file you changed.

Go over the changes before publishing, and run the check below.

Check that nothing calls Google anymore

  1. Open your site in a private window.
  2. Open the developer tools and go to the Network tab.
  3. Reload the page and filter for fonts.g.

No requests to fonts.googleapis.com or fonts.gstatic.com should appear. If any do, the typical culprits are a theme or page builder that pulls in fonts on its own, an embedded widget like a map or a captcha, or a CSS file with an @import you overlooked.

Common mistakes

  • The font loads twice. Your own @font-face is in place, but the old Google Fonts <link> never left the page. Grep the entire project for fonts.googleapis.com.
  • **Preload without crossorigin.** Without it, the browser discards the preloaded file and fetches the font again.
  • A wrong path. A 404 on the URL in src makes the browser silently switch to a system font. Paste the font URL into your browser to verify it loads.
  • Faux bold. Declaring only a 400 file while using font-weight: 700 forces the browser to fatten the regular glyphs on its own, and the result looks smudgy. Declare each weight you actually use, or switch to a variable font.
  • Missing characters. Trim a file to basic Latin and your umlauts or accents render in the fallback font. Hold on to every character your content needs.
  • Fonts on another domain. Files hosted on a CDN or subdomain require that server to return an Access-Control-Allow-Origin header, otherwise browsers will not apply the fonts.

Questions people ask

Is it illegal to use Google Fonts? Not in general. What the Munich ruling covered was pulling fonts from Google's servers without the visitor's consent. Self-hosting sidesteps the issue entirely, since nothing gets sent to Google. This is not legal advice.

Do I have to credit the font on my site? Under the SIL Open Font License, no visible credit is required. What it does require is that the copyright notice and the license remain alongside the font files, so keep the license file next to them.

Will my site be faster? Usually a little, since the browser no longer needs a connection to another server. The exact gain depends on your site, so measure before and after with Lighthouse or the Network tab.

Does next/font with Google Fonts send data to Google? Not from your visitors. Next.js grabs the files at build time, and your visitors receive them from your own domain.

What about fonts nobody else has? Any font you hold the rights to use can be self-hosted, even one created specifically for your project.

One more reason to host your own fonts

After your fonts live on your own server, you stop being stuck with the fonts everybody else has. Any font you hold the rights to works the same way, even one created just for your project. That is what we are building at MakeFont: your agent designs a typeface for your project and hands it over as files you host exactly as described here. It is in development, and early access is open. Read more in What is MakeFont.