自托管字体(self hosted fonts):把网页字体搬到自己的服务器上

self hosted fonts 是把网页字体文件放在自己域名下、不走 Google Fonts 这类第三方。这篇讲清你搬走了哪一段请求链、@font-face 与 preload 两段可粘代码,以及一个能防止字体切换顶动布局的兜底做法。

改写与发布6 分钟读完1764 次阅读
自托管字体(self hosted fonts):把网页字体搬到自己的服务器上

自托管字体(self hosted fonts)指的是把网页字体文件放在自己的域名下,而不是走 Google Fonts 这类第三方。它值不值得做,只看一件具体的事:少一个域名要解析。浏览器打开页面的那条连接连的是你自己的服务器,字体放在同一个域名,DNS 和 TLS 都能复用;放在别人家,就得先连过去、下一个样式表、读出里面的 @font-face,才知道字体文件在哪。代价是子集化、缓存和授权这三件事得自己扛。

先读这篇

你需要能往站点里加文件、能改 HTML 或 CSS。不需要构建工具。这篇默认你知道为什么文档头部很贵;不清楚的话先看 render blocking resources。这条改动最终喂给的是 largest contentful paint;真实网站到底怎么放字体,数字在 26 个首页的字体实测里,和本篇是方法与数字的两半。

为什么 self hosted fonts 改变关键路径

链到 Google Fonts 花的请求不止一个。浏览器先连 fonts.googleapis.com 拉一个样式表,解析出里面的 @font-face,再去连 fonts.gstatic.com 拿真正的字体文件。两个域名、两次 DNS、两次 TLS,还是一条链:样式表没到、没解析完,字体文件根本不会开始下。把文件搬到自己域名,新增域名数就是零。

原因是你的页面本来就连着自己的域名。HTML 从这里来,DNS 答案在缓存里,TLS 会话是开着的。同源的字体把这两样都复用掉;第三方的字体复用不了,而且浏览器在下载并解析完别人家的 CSS 之前,压根不知道有这个字体。

自托管字体,是浏览器画第一笔之前要信的域名少了一个。

自托管到底省下什么

省的不是字节。字体文件不管你发还是 Google 发都一样大,Google 的 CDN 也很快。省的是往返次数和依赖关系。下面这张表是「画第一个字形之前」的请求链。

环节Google Fonts自托管
新解析域名2 个0 个
新 TLS 握手2 次0 次
字体文件之前1 个样式表0 个
谁能改谁能挡第三方你自己

这张表带出两件事。第一,首屏不再等一个别人家的样式表。第二,你不再把每个访客的 IP 都交给那家公司——隐私评估里被反复问到的正是这一条。两件事单独都不构成排名因素;它们改变的是冷连接下页面的手感,而指标最差的时候通常就是冷连接。

按这个顺序做

六步,每一步都有一个可查的完成标志。第一步别跳,它决定你会不会踩授权。

  1. 逐个确认字体的授权。Google Fonts 家族按开源许可发布(含 SIL OFL 与 Apache 2.0),允许再分发;foundry 字体和付费字体往往不允许,网页字体授权通常按访问量计价。完成标志:每个文件都能指到它的许可。
  2. 下 woff2 文件。从字体自己的站点、Fontsource 这类包、或提供方的下载页拿。完成标志:文件在本地,按家族与字重命名。
  3. 裁到你会用到的字符集。一套完整的中文字体动辄几 MB,拉丁子集常在 30 KB 以内。完成标志:文件小到你能报出它的体积。
  4. 放到自己的域名下。任何你能控制的路径都行,包括在自家主机前面挂 CDN。关键在 origin,不在那块盘。完成标志:直接请求这个文件从你的域名返回 200。
  5. 写 @font-face,并预加载首屏要用的那一个文件。两段都在下面的交付物里。完成标志:网络面板显示字体从你的域名到达,并且没有发往 fonts.googleapis.com 的请求。
  6. 删掉旧的 link。移除 Google Fonts 的样式表链接,以及指向它的 preconnect。完成标志:全新加载对第三方域名的请求为 0。

交付物:两段可以直接粘

self hosted fonts 的全部落地动作就是下面两段。第一段是字体本身的规则。unicode-range 那行可选,但它能让浏览器不去下永远用不到的范围。font-display 决定文件还在路上时怎么办,对读者正在读的正文来说 swap 是安全的默认值。

@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;
}

第二段放进头部,让浏览器在解析到引用它的样式表之前就开始下字体。字体的 preload 必须带 crossorigin,即使同源也一样——字体是按 CORS 模式抓取的。

<link rel="preload" href="/fonts/inter-latin.woff2" as="font"
      type="font/woff2" crossorigin>

预加载只有在那个字体首屏要用时才划算。全预加载等于让浏览器跟自己抢带宽,用下面这张表来决定。没进表的照样会加载,只是晚一点。

字体角色预加载原因
首屏标题字体要首屏就要画,晚到就是肉眼可见
正文字体不要文件没到时兜底字体够用一会
同家族第二个字重不要首屏通常只用一个字重
图标或装饰字体不要很少进首屏,有时根本不用

做错了会怎样

三种失败反复出现。它们都不报错,所以值得点名。

  1. 搬了文件却没配兜底字体。没有度量接近的兜底字体,从兜底切到你的字体会把文字顶动,表现出来就是布局偏移。挑一个字宽接近的系统字体,能设 size-adjust 就设。
  2. 发了没裁的字体。一套完整中文字体常在 3 MB 以上。为了省两个往返去下一个这么大的文件,在手机上是很差的交换。先裁,再测。
  3. 删了 link 却留着 preload。没有对应 @font-face 的 preload 是一次白下的请求,浏览器还会打警告。两段要同步。

常见问题

自托管字体对 SEO 更好吗?

本身不会。没有任何字段奖励字体文件的来源。只有当它减掉了关键路径上的工作、或修掉了一次布局偏移,才会有帮助,而这两件事喂的是页面体验信号。把它当性能修复,不要当排名技巧。

google fonts 怎么自托管?

把要用的家族与字重的 woff2 下下来,脚本大就先裁子集,放到自己域名,加上上面那段 @font-face,预加载首屏要用的那一个文件,然后删掉 Google Fonts 的 link 和它的 preconnect。验收标准是全新加载时对 Google 字体域名的请求为 0。

自托管字体会改善 Core Web Vitals 吗?

能推动数字,但没有固定目标值。移掉一个阻塞样式表可能把 largest contentful paint 提前,好的兜底字体让累积布局偏移更小。提升多少取决于你原来的设置、连接和缓存,所以要改前改后各测一次,而不是想当然。

自托管网页字体合法吗?

取决于每一个字体自己的许可,跟你从哪儿下到的无关。开源字体许可(OFL、Apache 2.0)通常允许再分发;商业字体往往不允许,少数只允许按访问量的方案。这一步我们不做简化:每一个文件都读一遍许可。

自托管会不会把站点改坏?

会,在三个地方。CSS 引用了你没定义的字体名,文字会静默回退;删掉一个其实在用的字重,浏览器会伪粗或伪斜,字看着变形;在新文件上线之前就删了第三方,中间那段时间所有访客都会闪一下无样式字体。

边界说清楚:这篇讲的是文件从哪儿来,不是选哪款字体。我们没有测过自托管的排名影响,也没有一个固定的省下多少毫秒的数字,因为它取决于你现有的缓存、CDN 和冷启动。在很快的 CDN 加命中缓存的情况下,差别可能不到一百毫秒、几乎看不出来。能一直成立的是「少一个第三方域名」这件事。把它当成 QueryWin 处理渲染这条链里的一环是合理的,但授权检查要放在最前面。

本文属于 QueryWin 实操手册 · 第 2 阶

自托管字体(self hosted fonts):把网页字体搬到自己的服务器上