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

▶ The whole swap in 3 minutes 48 seconds, narrated, with captions — including the two domains to search for, the stray preconnect that keeps the line to Google open, and why a clean code search is not proof.

Read the narration instead

The shape of it. Loading fonts straight from Google can put your visitors' IP addresses on Google's servers without their consent. Bunny Fonts is the drop-in fix: the same fonts, the same URL shape, nothing sent to Google. About ten minutes, and it is free.

What it actually fixes. When a browser loads a Google-hosted font it quietly sends the visitor's IP 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.

Step one: find every place it loads. Search the whole project for two domains, not one — fonts.googleapis.com serves the stylesheet and fonts.gstatic.com serves the font files. Both are Google, both leak the IP, and missing one leaves the leak open.

Four predictable places: a <link> tag in your <head>; an @import at the top of a CSS or SCSS file; a theme or page-builder setting, since many bundle Google Fonts by default; and an npm package that fetches from Google at runtime.

Step two: the swap. Change the domain to fonts.bunny.net, put the family in lower case with hyphens, list weights after a colon rather than Google's syntax, and join families with a pipe. display=swap stays as it is. Bunny mirrors the whole catalogue, so your families and weights are all there.

Step three, and the bit people forget. Add a preconnect to Bunny above the stylesheet link — then delete any leftover preconnect pointing at a Google font domain. A preconnect opens a connection to that host on its own, even when nothing else loads from it, so a stray one keeps the line to Google open after you have swapped everything else.

Step four: check it has really gone. Run the same search again; it should come back completely empty. This is the step people skip, and it is the one that matters, because a single missed reference means the leak is still open.

A clean search is not proof. 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: open dev tools, filter for font, reload, and confirm every request goes to Bunny and none to a Google domain.

Why it is worth ten minutes. In January 2022 a regional court in Munich ruled against a website operator over exactly this — fonts loaded from Google, and a visitor's IP transferred without consent. The guide sets out the detail in a form you can hand to a client or a colleague.

And visually, nothing changes. Bunny serves the very same font files from a privacy-first, cookie-free European service that logs no personal data. Same families, same weights, same rendering. You change one domain, and the leak stops.

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/css2 → fonts.bunny.net/css
Family nameTitle case → lower case with hyphens (Outfit → outfit, JetBrains Mono → jetbrains-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.

On PageMotor 0.10 and later, this is built in. Open Site Settings, find Google Fonts 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. One setting swaps both the fonts API URL and the preconnect host, site-wide.

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

The EP Bunny Fonts plugin was retired in July 2026, now that the same switch ships in PageMotor core. It is no longer distributed. If a pre-0.10 site still has it installed, it keeps working, but the upgrade path is PageMotor 0.10+ and the Site Settings option above.

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