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.
grep). No new accounts, no API keys, and nothing to pay. Bunny Fonts is free.
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 promptBefore 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:
<link> tag in your HTML <head>.@import at the top of a CSS or SCSS file.@fontsource-google/*, that fetches from Google at runtime.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.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:
| Part | Google → Bunny |
|---|---|
| Domain | fonts.googleapis.com/css2 → fonts.bunny.net/css |
| Family name | Title case → lower case with hyphens (Outfit → outfit, JetBrains Mono → jetbrains-mono) |
| Weights | :wght@400;600 → :400,600 |
| More than one family | join with a pipe, | |
display=swap | keep 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");
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.
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.
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.
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';
@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.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.
The most common worry, answered.
display=swap works identically, so text shows immediately and the web font swaps in when it is ready.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.
The few things that trip people up, and the fix for each.
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.
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.
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.
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.