ElmsPark Guides
Privacy & GDPR guide

Replacing Google Fonts with Bunny Fonts

Loading fonts straight from Google can put your visitors’ IP addresses on Google’s servers without their consent. In the EU that is a real legal risk. Bunny Fonts is the drop-in fix: the same fonts, the same URL shape, nothing sent to Google. Here is the ten-minute swap.

About 10 minutes 🛡 Closes a real GDPR gap 🔤 Your fonts look identical 💻 Any site, any framework
What you’ll need: access to your site’s code or theme, and a way to search it (your editor’s find-in-files, or grep). No new accounts, no API keys, and nothing to pay. Bunny Fonts is free.
What this actually fixes. When a browser loads a Google-hosted font, it quietly sends the visitor’s IP address to Google, before any cookie banner can ask permission. An IP address is personal data under the GDPR, so that transfer needs a lawful basis you almost certainly do not have. Bunny Fonts serves the very same font files from a privacy-first, cookie-free European service that logs no personal data. You change one domain, and the leak stops.

Use this guide with any AI assistant

Download it as a prompt file, paste it into Claude, ChatGPT, Gemini or any LLM, and it will walk you through the swap step by step.

↓  Download as LLM prompt

1Find where Google Fonts loads

Before changing anything, find every place Google Fonts is pulled in. There are usually only one or two, but missing one leaves the leak open. Search your whole project for the two Google font domains:

$ grep -rn "fonts.googleapis.com\|fonts.gstatic.com" .

They turn up in a handful of predictable places:

Two domains, not one. fonts.googleapis.com serves the stylesheet; fonts.gstatic.com serves the actual font files. Both are Google, and both leak the visitor’s IP. Search for both.

2Swap the stylesheet link

Bunny Fonts mirrors the whole Google Fonts catalogue, so the families and weights you already use are all there. The URL is almost the same shape: change the domain, lower-case the family name, and list the weights after a colon.

Here is a typical Google Fonts link:

<!-- before: Google -->
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600&display=swap" rel="stylesheet">

And the Bunny Fonts equivalent:

<!-- after: Bunny -->
<link href="https://fonts.bunny.net/css?family=outfit:400,600&display=swap" rel="stylesheet">

The translation rules, line by line:

PartGoogle → Bunny
Domainfonts.googleapis.com/css2fonts.bunny.net/css
Family nameTitle case → lower case with hyphens (Outfitoutfit, JetBrains Monojetbrains-mono)
Weights:wght@400;600:400,600
More than one familyjoin with a pipe, |
display=swapkeep it, unchanged

So two families together look like this:

<link href="https://fonts.bunny.net/css?family=outfit:400,600|jetbrains-mono:400&display=swap" rel="stylesheet">

Doing the same in CSS? Swap the @import URL the same way:

@import url("https://fonts.bunny.net/css?family=outfit:400,600&display=swap");
Not sure of the exact family slug? Browse fonts.bunny.net and copy the embed line straight from there, just as you would on Google Fonts.

3Add a preconnect

One small line tells the browser to open the connection to Bunny early, so the fonts arrive a little faster and you avoid a flash of unstyled text. Put it in your <head>, just above the stylesheet link:

<link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
<link href="https://fonts.bunny.net/css?family=outfit:400,600&display=swap" rel="stylesheet">

While you are there, delete any leftover preconnect pointing at a Google font domain. A preconnect opens a connection to that host on its own, even if nothing else loads from it, so a stray one keeps the line to Google open.

4Check it has really gone

This is the step people skip, and it is the one that matters. A single missed reference means the leak is still open. Two quick proofs:

Search the code again. The same search should now come back empty:

$ grep -rn "fonts.googleapis.com\|fonts.gstatic.com" .
# no output = nothing left pointing at Google

Watch the network. Open the page, open your browser’s developer tools, go to the Network tab and filter for font. Reload. Every request should go to fonts.bunny.net, and not one to a Google domain.

Don’t trust a clean code search alone. Themes, plugins and embedded widgets can inject a Google Fonts tag at runtime that never appears in your source. The Network tab is the ground truth: if no request hits a Google domain there, the leak is closed. If one does, trace it back to the theme or plugin that added it.

The legal risk, briefly

Worth understanding, so you can explain it to a client or a colleague.

In January 2022 a regional court in Munich (Landgericht München I) ruled that a website which embedded Google Fonts from Google’s servers had unlawfully passed the visitor’s IP address to Google in the United States, without consent. The site owner was ordered to pay damages to the visitor who had complained.

The ruling itself was small, a single case and a modest sum, but it confirmed a principle that touches almost every website: an IP address is personal data under the GDPR, and sending it to a US company by default, before any consent is given, has no lawful basis. Waves of warning letters followed across Germany and the wider EU.

You cannot paper over this with a cookie banner, because the font request fires as the page loads, before the visitor can agree to anything. The only reliable fix is to stop sending the request to Google. That is exactly what swapping to Bunny Fonts, or self-hosting, does.

Is Bunny Fonts actually compliant? Bunny Fonts is run by bunny.net, a European company, built specifically as a privacy-first mirror. It sets no cookies, logs no personal data and exists for this exact problem. It is the standard drop-in answer the privacy community points to.

Self-hosting: zero third-party requests

The most airtight option: serve the font files from your own domain, so no third party is involved at all.

Bunny Fonts closes the Google leak and is enough for most sites. If you want to remove the third party entirely, host the files yourself. In a project with npm, the Fontsource packages make this painless: they bundle the actual font files into your build.

$ npm install @fontsource/outfit

Then import the weights you need, once, in your app’s entry point:

// main.js / app entry
import '@fontsource/outfit/400.css';
import '@fontsource/outfit/600.css';
Mind the package name. Use @fontsource/<family>, which bundles the files locally. Avoid @fontsource-google/*, a different set of packages that fetch from Google’s CDN at runtime, which puts the leak straight back.

On PageMotor

If you build on PageMotor, there is a tidier route than hand-editing the head.

Use the EP Bunny Fonts plugin. It registers your chosen families through PageMotor’s head valet, so the Bunny stylesheet and preconnect are emitted on every page without you embedding anything by hand, and without a theme update wiping your change. Pick the families and weights in its settings, and it handles the markup.

This is the recommended approach for any PageMotor theme or plugin: keep font loading in one place that survives updates, rather than scattered across template files.

On PageMotor 0.10 and later, this is built in. Open Site Settings, find Font Delivery Service, and switch it from Google to Bunny Fonts. Every font your site loads then comes from Bunny’s EU CDN, with no plugin and nothing to embed by hand. The separate EP Bunny Fonts plugin is deprecated from 0.10, so on a 0.10 install use this Site Settings option instead. On 0.9 and earlier, the plugin above is still the right tool.

What changes visually: nothing

The most common worry, answered.

In practice the only thing that changes is the domain in one or two lines of markup, and a request that no longer goes to Google.

Troubleshooting

The few things that trip people up, and the fix for each.

My fonts stopped loading after the swap

Almost always the family slug or weight syntax. Bunny wants the family in lower case with hyphens (jetbrains-mono, not JetBrains Mono) and weights after a single colon, comma-separated (:400,600). Open fonts.bunny.net, find the family, and copy its embed line exactly.

The Network tab still shows a request to Google

Something is injecting it at runtime, usually a theme, a page builder or a plugin. Disable suspects one at a time, or search the rendered page source (not just your code) for googleapis. On WordPress, many themes have a setting to disable their bundled Google Fonts; turn it off and load Bunny yourself.

A brief flash of an unstyled or different font

That is the normal display=swap behaviour: the browser shows a fallback, then swaps in the web font. The preconnect from step 3 shortens it. To remove it entirely, self-host with Fontsource and use font-display: optional.

Does this apply to icon fonts and Material Symbols too?

Yes. Material Icons and Material Symbols are served from the same Google domains and leak the same way. Bunny mirrors them, and self-hosting works too. Treat them exactly like any other Google-hosted font.

See also