Self hosted fonts: how to serve web fonts from your own server
Self hosted fonts are web font files served from your own domain instead of a third party like Google Fonts. This chapter shows the request chain you remove, the @font-face and preload blocks to paste, and the one fallback that stops a swap from moving the layout.

Self hosted fonts are web font files served from your own domain instead of a third party such as Google Fonts. Moving them is worth it for one concrete reason: it removes an entire origin from the critical path, so the browser never has to resolve a second host before it can fetch the glyphs. It is not automatically faster, and the work is small, but you take on subsetting, caching and the licence.
Read this first
You need the ability to add files to your site and edit its HTML or CSS. No build step is required. This chapter assumes you know why the head of a document is expensive; if not, render blocking resources explains the mechanism. The number this work feeds is largest contentful paint, and for what real sites actually do with their fonts, fonts across 26 homepages is the measured companion to this chapter.
Why self hosted fonts change the critical path
A link to Google Fonts costs more than one request. The browser opens a connection to fonts.googleapis.com, downloads a stylesheet, parses an @font-face rule inside it, then opens a second connection to fonts.gstatic.com for the actual font file. That is two hosts, two DNS lookups and two TLS handshakes, and it is a chain: the font cannot start downloading until the stylesheet has arrived and been read. Serving the files yourself collapses the chain to zero new hosts.
The reason is that your page is already connected to your own domain. The HTML came from there, so the DNS answer is in cache and the TLS session is open. A font on the same origin reuses both. A font on a third-party origin cannot, and the browser has no way to know the font exists until it has downloaded and parsed a CSS file from a different company.
A self hosted font is one less origin the browser has to trust before it can paint.
What self hosting actually saves
The saving is not the bytes. A font file is the same size whether you or Google sends it, and Google's CDN is fast. The saving is the round trips and the dependency. The table below is the request chain before the first glyph can be drawn.
| Step | Google Fonts link | Self hosted |
|---|---|---|
| New hosts to resolve | 2 | 0 |
| New TLS handshakes | 2 | 0 |
| Requests before the font file | 1 stylesheet | 0 |
| Who can change or block it | the provider | you |
Two effects follow from that table. First, the first paint no longer waits on a stylesheet from a second company. Second, you stop leaking every visitor's IP address to that company, which is the part that gets raised in privacy reviews. Neither effect is a ranking factor on its own; both change how the page feels on a cold connection, which is where the metrics are usually worst.
Do it in this order
Six steps, each with something you can check before moving on. Do not skip step one: it is the one that keeps you out of a licence problem.
- Check the licence of every font you plan to serve. Most Google Fonts are under the Open Font License or Apache 2.0, which allow redistribution. Foundry and paid fonts often do not, and a web font licence is usually priced by pageviews. Done when you can point at the licence for each file.
- Download the
woff2files. Take them from the font's own site, from a package such as Fontsource, or from the provider's download page. Done when each file is on disk and named after the family and weight. - Subset the files to the characters you use. A full CJK face can run to several megabytes; a Latin subset is often under 30 KB. Use a subsetting tool, or download a pre-subset Latin file. Done when the files are small enough that you can name their size.
- Put them on your own domain. Any path you control works, including a CDN in front of your own host. The point is the origin, not the disk. Done when a direct request to the file returns
200from your domain. - Write the
@font-facerule and preload the one file the first screen needs. Both blocks are in the deliverable below. Done when the network panel shows the font arriving from your domain and no request tofonts.googleapis.com. - Delete the old link tags. Remove the Google Fonts stylesheet link and any
preconnecthints that point at it. Done when a fresh load has zero requests to the third party.
The deliverable: two blocks you can paste
The first block is the rule for the font itself. The unicode-range line is optional but it keeps the browser from downloading a range it will never use. The font-display value decides what happens while the file is still arriving, and swap is the safe default for text the reader is waiting to read.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+0131, U+2000-206F;
}
The second block goes in the head and tells the browser to start the font download early, before it has parsed the stylesheet that references it. A font preload always needs crossorigin, even on the same origin, because fonts are fetched in CORS mode.
<link rel="preload" href="/fonts/inter-latin.woff2" as="font"
type="font/woff2" crossorigin>
Preloading helps only when the font is needed for the first screen. Preloading everything makes the browser compete with itself, so use the table below to decide. Everything left off it still loads, just later.
| Font role | Preload | Why |
|---|---|---|
| Heading font above the fold | Yes | It is painted on the first screen, so late means visible |
| Body text font | No | The fallback is fine for the moment the file is missing |
| Second weight of the same family | No | The first screen usually uses one weight |
| Icon or decorative font | No | Rarely in the first paint; sometimes not at all |
What goes wrong
Three failures show up again and again. None of them throws an error, which is why they are worth naming.
- Self hosting the file but forgetting the fallback. Without a metric-compatible fallback, the swap from the fallback font to your font moves the text, and that shows up as layout shift. Pick a system font whose letter widths are close, and set its size-adjust if you can.
- Serving an unsubsetted font. A full CJK face is often 3 MB or more. Downloading it to save two round trips is a bad trade on a phone. Subset first, then measure.
- Removing the link but leaving the preload. A preload with no matching
@font-faceis a wasted download and the browser prints a warning. Keep the two blocks in step.
Common questions
Are self hosted fonts better for SEO?
Not by themselves. There is no field that rewards the file's origin. They help only if they remove work from the critical path or fix a layout shift, and both of those feed page experience signals. Treat the change as a performance fix, not a ranking trick.
How do I self host Google Fonts?
Download the woff2 files for the family and weights you use, subset them if the script is large, place them on your domain, add the @font-face rule above, preload the one file the first screen needs, then delete the Google Fonts link and its preconnect. The check is a fresh load with zero requests to a Google font host.
Do self hosted fonts improve Core Web Vitals?
They can move the numbers, but not to a fixed target. Removing a blocking stylesheet can pull the largest contentful paint earlier, and a good fallback keeps the cumulative layout shift smaller. The size of the gain depends on your previous setup, the connection and the cache, so measure before and after rather than assuming.
Is it legal to self host web fonts?
It depends on the licence of each font, not on where you found it. Open Font License and Apache 2.0 fonts can usually be redistributed. Commercial fonts often cannot, and a handful allow it only on a pageview plan. This is the one step we will not simplify: read the licence for every file.
Will self hosting break anything on my site?
It can, in three places. If a CSS rule refers to a font family name you did not define, the text falls back silently. If you drop a weight you actually use, the browser fakes it and the text looks thin or heavy. If you remove the third party before the new files are live, the page flashes an unstyled font for everyone in the interval.
The boundary worth saying plainly: this chapter is about where the file is served from, not about choosing a typeface. We have not measured the ranking effect of self hosting, and we do not have a number for the milliseconds it saves, because that depends on your existing caching, CDN and cold-start behaviour. On a fast CDN with a warm cache the difference can be under a hundred milliseconds and hard to see at all. Removing the third-party origin is the part that always holds. Doing it as part of the wider work on how QueryWin approaches rendering is reasonable, but the licence check comes first.
Part of the QueryWin handbook · Level 2


