Self-hosting fonts, and other small wins for Core Web Vitals
Most WordPress themes ship with Google Fonts loaded from Google’s CDN. The theme calls a stylesheet from fonts.googleapis.com, which calls font files from fonts.gstatic.com, and your site gets the typography the designer chose. This is the default, and it has been the default for so long that most theme authors don’t even think about it. Self-hosting fonts is the boring fix that most clinic sites should make.
It is also the wrong default for a clinic site. Not catastrophically wrong, but wrong enough to be worth fixing. The reasons are partly about Core Web Vitals, partly about privacy, and partly about the kind of slow drift that 18 months from now will make you regret leaving the default in place.
What loading a Google Font actually does
When a visitor opens your page, the browser parses the HTML, finds the link to Google’s stylesheet, and opens a connection to fonts.googleapis.com. That returns a CSS file with @font-face rules pointing at fonts.gstatic.com. The browser then opens a second connection to fetch the actual font files. Two DNS lookups, two TLS handshakes, two round trips before the text can render.
Browsers have gotten clever about this. Modern HTTP/2 connections are fast. Google’s CDN is close to most users. The penalty is small, usually 100 to 300 milliseconds depending on geography. On a fast connection from a major city, you won’t notice. On a phone with 4G in a hospital parking lot, you might.
The Core Web Vitals angle
The metric that catches font loading is Largest Contentful Paint. LCP measures how long it takes for the largest visible element to render. On most clinic homepages, that is a hero headline. The headline can’t render until its font has loaded. So a font fetched from a third-party origin adds directly to LCP.
Self-hosting the same font as a static file on your own server cuts out one DNS lookup, one TLS handshake, and one round trip. The font file is served from the same connection your HTML and CSS came from. The savings vary, but I see 150 to 400ms shaved off LCP on real clinic sites when I move from Google’s CDN to local hosting.
If your LCP is currently 2.8 seconds and you need it under 2.5 to pass Google’s threshold, that 300ms is the difference between a passing site and a failing one. For most clinic sites I audit, this is the single highest-leverage change available.
The privacy angle
Every request to fonts.googleapis.com or fonts.gstatic.com tells Google that someone is visiting your site. The request includes the user’s IP address and a Referer header pointing at your page. Google does not promise much about what it does with that data. It does not promise nothing, either.
For a marketing site selling t-shirts, this is fine. For a pediatric clinic where the user might be a parent searching for help with a sensitive issue, it is not. A German court ruled in 2022 that embedding Google Fonts without consent violated GDPR. The ruling does not bind US sites, but the underlying logic applies anywhere privacy matters: if you can avoid sending visitor data to a third party, you should.
I wrote about a related principle in third-party scripts on a clinic site. Every external request is a potential leak. Fonts are the easiest one to plug.
How to actually do self-hosting fonts on WordPress
The mechanical steps are short.
First, identify which fonts you actually use. Most themes ship loading three or four font families with multiple weights, but the site uses two. Open the front-end in DevTools, watch the Network tab, see which font files actually fetch on a typical page. Drop the rest.
Second, download those exact fonts. The google-webfonts-helper tool (gwfh.mranftl.com) lets you specify the family, weights, and subsets you need, and it gives you back a zip of woff2 files plus the @font-face CSS rules. The woff2 files go in your theme or upload directory. The CSS goes in your stylesheet.
Third, remove the enqueue or link tag that loads from Google. In a typical theme this means commenting out a wp_enqueue_style call or removing a hardcoded link in header.php. Test that the front-end still looks right.
Fourth, add preload hints. In your theme’s header, add a link rel=”preload” tag for the most-used font weight. This tells the browser to start fetching that font as early as possible, before the CSS parser even reaches the @font-face rule.
Other small wins worth doing while you are at it
font-display: swap. This tells the browser to render text with a fallback font immediately, then swap in the custom font when it loads. Without this, the browser shows nothing until the font arrives, which is worse for both LCP and the user experience. Google’s hosted fonts include this by default. When you self-host, make sure your @font-face rules include it.
Subset to what you need. A full Latin character set is fine for English. The extended Latin set adds 30 to 50 KB per weight for characters you don’t use. If your clinic serves an English-speaking community, the basic Latin subset is enough.
Drop weights you don’t use. Many themes load 300, 400, 500, 600, and 700 weights of a family. You probably use two or three. Each unused weight is 20 to 40 KB of font data the browser fetches and parses for no reason.
Consider system fonts for body text. If your design can tolerate it, using -apple-system, BlinkMacSystemFont, “Segoe UI”, system-ui for body text gives you zero font load. The custom font is reserved for headlines, where its character matters. This is the approach we use on the About page here.
The cumulative effect
Self-hosting fonts is not glamorous. It will not show up on a sales page or a portfolio. It is a 30-minute change that improves LCP by a few hundred milliseconds, drops a third-party dependency, and slightly improves the privacy story of your site.
The reason it matters is that small wins compound. Self-hosted fonts, an Nginx FastCGI cache configured correctly, a CDN serving static assets without aggressive HTML caching, and a clean plugin list together add up to a site that feels fast on a phone in a parking lot. Each piece is small. The result is a site that loads quickly without you thinking about it, which is the only kind of fast that matters.
