Self-hosted fonts in Next.js, and why German sites care
In January 2022 a Munich court ruled that embedding Google Fonts the usual way, with a <link> to fonts.googleapis.com, passes the visitor's IP address to a US company without consent, and that this was a violation of the visitor's rights. A wave of warning letters followed. Thousands of site owners got mail asking for a hundred euros and a signature. Most of them had no idea their page was making that request at all, because a font is the kind of thing you set up once and never look at again.
The fix is not to stop using the fonts. The fix is to serve them yourself. Since Next.js 13 that is one import away.
What next/font does
next/font/google downloads the font files during next build, puts them in your static output and generates the @font-face rules that point at your own origin. At runtime the browser asks your server for the font and nothing else. Google is involved at build time on your machine or your CI runner, never in the visitor's browser.
// app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin", "latin-ext"],
display: "swap",
variable: "--font-sans",
});
export default function RootLayout({ children }) {
return (
<html lang="de" className={inter.variable}>
<body>{children}</body>
</html>
);
}
Three small things worth getting right in that block. subsets decides which glyph ranges get bundled; German needs latin-ext for nothing in particular, but if you ever write a Polish or Turkish name you will be glad it is there. display: "swap" shows text in a fallback font until the real one arrives, which keeps the page readable on a slow connection. The CSS variable lets you reference the font from plain CSS without importing anything else, which matters if you avoid Tailwind like I do.
If you bought a font instead of picking a free one, next/font/local does the same job from a file in your repo. Same API, same build-time handling.
How to check it actually worked
Do not trust the import. Open the finished site, open the network tab, filter by "font", reload. Every font request should go to your own domain. If anything points at fonts.googleapis.com or fonts.gstatic.com, something else on the page is still loading fonts the old way. The usual suspects are a third-party widget, an old global stylesheet with an @import line at the top, or a component library that brings its own font link.
A quicker check from the terminal: fetch the page HTML with curl and grep for googleapis. Zero hits is the goal.
Why I bother on an English site too
The site you are reading is English by default with a German mirror. The legal risk only applies to the German visitors, but there is no reason to run two font strategies. Self-hosting is also faster: one fewer DNS lookup, one fewer TLS handshake, and the font arrives from the same CDN edge as the page. It is the rare case where the privacy-correct option is also the performance-correct one, so there is nothing to weigh.
