如何在本地托管字体,而不是使用 Google Fonts
一份兼顾隐私的实用指南,介绍如何从自己的域名下载、子集化、提供并测试 Web 字体。
目录
为什么要自行托管 Google Fonts?
Google Fonts 让优秀的排版变得简单。添加一个样式表,选择几个字重,然后发布页面。多年来,这一直是小团队的合理默认选择。
代价是,每位访问者的浏览器都会联系第三方服务,以获取字体 CSS 和字体文件。这会带来两个后果。
首先,它给渲染增加了一个外部依赖。如果字体 CSS 很慢、被阻止,或在用户所在地区或网络中不可用,你的页面就会等待或回退。
其次,它带来了隐私问题。一次字体请求可能会向第三方暴露用户的 IP 地址、用户代理、referrer policy 上下文以及时间信息。Google Fonts 声明它不会通过 Fonts API 设置 cookie,但“没有 cookie”并不等于“没有个人数据”。在 GDPR 下,IP 地址在特定语境中仍可能属于个人数据。
自行托管字体并不是每个网站都自动必须做的事,本文也不是法律建议。但对于欧洲网站、公共部门网站、医疗、教育、金融,或任何试图减少不必要第三方请求的团队来说,本地托管通常是更干净的选择。
如果做得好,它通常也能带来性能收益。关键在于“做得好”。把六个字体文件复制到 /assets/fonts/,然后在每个页面上全部加载,可能比使用托管服务更糟。如果你想了解更广泛的性能背景,我们之前关于为什么 web fonts are still the easiest performance win on most sites 的文章介绍了常见的浪费模式。
自行托管后会发生什么变化
当你以常规方式使用 Google Fonts 时,页面会这样做:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
浏览器会先从 fonts.googleapis.com 请求 CSS,然后从 fonts.gstatic.com 下载字体文件。
自行托管时,你的页面应当从自己的域名请求 CSS 和字体文件:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
这会移除第三方字体请求。同时,你也需要负责选择文件格式、缓存头、回退字体以及更新。
这份责任值得认真对待。字体位于关键渲染路径上。糟糕的字体配置可能导致文本不可见、布局偏移以及首次渲染缓慢。
第 1 步:审计你实际使用的内容
在下载任何内容之前,先列出网站真正需要的字体家族、字重、样式和字符集。
一个典型的营销网站可能需要:
- 正文使用 Regular 400
- 标题和按钮使用 Semibold 600 或 bold 700
- 只有在设计确实使用斜体时,才需要 Italic 400
- 仅使用 Latin 字符集,除非网站支持更多语言
要警惕旧的设计系统默认值。许多网站加载 300、400、500、600、700、斜体以及多个文字系统,只是因为有人曾经在字体选择器中选中过它们。
在浏览器 DevTools 中打开 Network 面板,按“font”过滤,重新加载页面,然后检查请求了哪些文件。接着检查 CSS 中的 font-weight 使用情况。如果你的 CSS 从未使用 300,就不要托管 300。
如果你之后要评估影响,Lighthouse 可以提供帮助,但不要把它的分数当作全部事实。把它作为诊断工具,而不是裁判。我们有一份单独的指南,介绍如何 reading a Lighthouse report without panicking,在确定字体修复优先级时会很有用。
第 2 步:下载正确的字体文件
Google Fonts 提供开源字体。你可以从 Google Fonts 网站或相关字体项目仓库下载它们。请检查许可证,但大多数 Google Fonts 都根据 SIL Open Font License 或 Apache License 等开放许可证分发。
对于 Web,优先使用 WOFF2。现代浏览器广泛支持它,而且通常比 TTF 或 OTF 小得多。在 2026 年,直接向浏览器提供 TTF 对公共网站来说很少有充分理由。
一个合理的目录结构如下:
/public
/fonts
inter-latin-400.woff2
inter-latin-600.woff2
inter-latin-700.woff2
使用描述性文件名。六个月后,font.woff2 会很恼人。inter-latin-600.woff2 枯燥但有用。
如果你的网站使用构建系统,请把源字体放在清晰的位置,并让构建流水线将优化后的文件复制到公共资源目录中。
第 3 步:在适当情况下对子集化字体
子集化意味着移除你不需要的字符。一个完整字体可能包含 Latin、Cyrillic、Greek、Vietnamese、符号以及许多 OpenType 功能。如果你的纯英文落地页只需要 Latin 字符,子集文件可以小很多。
常见做法有两种:
- 使用字体提供方或仓库提供的预构建子集。
- 使用 fonttools 中的
pyftsubset等字体工具生成自己的子集。
对许多团队来说,预构建的 Latin 子集已经足够。自定义子集适用于文本非常受限的页面,例如只有有限文本的单个活动页面,或字符覆盖范围可预测的产品 UI。
多语言网站要谨慎。缺失字形会导致混用回退字体,看起来可能像是出错,并损害可读性。如果你支持多种语言,应将字体子集映射到语言路由,而不是在所有地方强制使用一个很小的子集。
第 4 步:编写你的 @font-face 规则
一个最小化的本地配置如下:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin-600.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
body {
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
这里有几个细节很重要。
对大多数内容型网站使用 font-display: swap。它会告诉浏览器先快速显示回退文本,然后在 Web 字体到达时进行替换。这可以避免最糟糕的 FOIT:不可见文本闪烁。
设置明确的回退字体栈。如果自定义字体加载失败,用户仍然应该看到可读的文本。回退字体不是事后补救;它们是设计的一部分。如果你需要重新审视字号、行长和正文文本选择,可以从 a practical guide to readable type on the modern web 开始。
正确匹配字重。如果你的 CSS 请求 font-weight: 500,但你只定义了 400 和 700,浏览器可能会合成一个中间字重。这并不总是很糟,但可能看起来不一致。
第 5 步:移除外部 Google Fonts 调用
添加本地字体 CSS 后,从模板中移除旧的远程调用。
查找:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?..." rel="stylesheet">
还要检查:
- CMS 平台中的主题设置
- 页面构建器的排版面板
- 第三方小组件
- 标签管理器
- 旧的 CSS 导入,例如
@import url('https://fonts.googleapis.com/...')
最后一项很常见。用于字体的 CSS @import 通常对性能更差,因为它会延迟发现。如果你自行托管,请在主 CSS 或较早加载的字体 CSS 文件中直接定义字体。
隐私工作经常失败,是因为团队修复了显眼的模板,却遗漏了脚本、小组件和旧嵌入。同样的模式也会出现在同意管理工作中;如果你正在更广泛地减少第三方接触面,我们关于 what changed for cookies in 2026 的指南会是有用的补充。
第 6 步:设置缓存头
字体文件是静态资源。如果文件名带有版本或内容哈希,就应该积极缓存它们。
一个良好的生产环境响应头是:
Cache-Control: public, max-age=31536000, immutable
只有当文件变化时 URL 也会变化时,才使用长期 immutable 缓存。例如:
inter-latin-400.a8f3c2.woff2
或带版本的路径:
/fonts/v2/inter-latin-400.woff2
如果你在不改变 URL 的情况下覆盖 /fonts/inter-latin-400.woff2,一些用户可能会长时间保留旧文件。没出问题时这没关系,直到它出问题。版本化可以避免这个问题。
同时,请使用正确的 MIME 类型提供字体:
Content-Type: font/woff2
大多数现代托管平台会自动处理这一点,但仍值得验证。
第 7 步:只考虑预加载关键字体
预加载可以帮助浏览器更早发现重要字体:
<link rel="preload" href="/fonts/inter-latin-400.woff2" as="font" type="font/woff2" crossorigin>
请谨慎使用。预加载首屏主要文本字体,而不是每一种字重。过度预加载会与 CSS、图片和 JavaScript 竞争。
即使是同源字体,也要在字体预加载中包含 crossorigin。字体获取使用 CORS 模式,省略它在某些配置中可能导致重复下载。
如果你不确定,就测试。不要因为某个清单这么说,就照搬预加载。
第 8 步:测试隐私和性能
测试很直接。
打开 DevTools,在禁用缓存的情况下重新加载页面,并在 Network 面板中过滤:
fonts.googleapis.comfonts.gstatic.com.woff2font
你应该看到字体文件来自自己的域名,并且没有 Google Fonts 请求。
然后分别用冷缓存和热缓存测试。首次访问时,字体应下载一次。之后访问时,它们应根据浏览器情况来自内存或磁盘缓存。
检查字体替换时是否发生布局偏移。如果标题跳动,说明你的回退字体度量与 Web 字体差异过大。你可以通过选择更接近的回退字体,或使用较新的 CSS 字体度量覆盖项来减少可见偏移,例如 size-adjust、ascent-override、descent-override 和 line-gap-override。这些更高级,但对打磨精致界面很有用。
最后,在隐私浏览模式或启用内容拦截器的情况下测试页面。自行托管的一个好处是,隐私工具不太可能意外阻止你的排版。
应避免的常见错误
托管过多字重
这是最常见的失败点。两个字重通常就够了。三个通常已经很充足。除非有充分理由,否则五个字重往往是设计系统异味。
忘记斜体
如果你的内容使用真正的强调,请加载真正的斜体文件。合成斜体可能看起来很差,尤其是在长篇编辑内容中。
保留旧的 Google CSS 链接
这会违背迁移的目的。迁移后,除非另一个组件正在注入,否则不应有任何字体请求发往 Google。
提供字体但没有长期缓存
自行托管给了你控制权。用好它。字体是长期缓存的理想候选。
忽略法律和文档工作
如果你的隐私政策之前提到过 Google Fonts 或第三方字体加载,请在迁移后更新它。如果你维护数据处理清单,也请同步更新。技术变更与合规记录应当一致。
<!-- tool-cta:start -->
💡 试试这个: 使用 Webfont Generator 将你从 Google Fonts 下载的 TTF 文件转换为可自行托管的 WOFF2 以及 CSS。
<!-- tool-cta:end -->
一个简单的迁移清单
- 列出你实际使用的字体家族、字重、样式和文字系统。
- 下载 WOFF2 文件并确认许可证。
- 如果网站语言需求有限,请对子集化字体。
- 添加带有
font-display: swap的本地@font-face规则。 - 移除所有 Google Fonts 的
link、preconnect和@import引用。 - 从自己的域名提供字体,并设置长期缓存头。
- 如果测试支持,只预加载最重要的首屏字体。
- 在 DevTools 中验证没有 Google Fonts 请求残留。
- 如有需要,更新隐私文档。
自行托管字体并不是什么光鲜的工作。它是一类小型基础设施清理,可以降低依赖风险,改善隐私保护态势,并让渲染更可预测。通常,这值得花上一两个小时。