字体防盗链:别让别人白嫖你的 woff2 流量-第三维度MokIII

为什么字体也要防盗链

图片防盗链大家都知道,字体常常被忘掉。但字体文件并不小——一个完整的 woff2 中文字库动辄几百 KB 到数 MB,别人引用你的字体,走的是你的流量、你的服务器。

判断有没有被盗用,看访问日志里字体请求的 Referer 即可:

grep -E '\.(woff2?|ttf|otf|eot)' /www/wwwlogs/example.com.log 
  | awk '{print $11}' | sort | uniq -c | sort -rn | head -20

$11 是请求行前的 Referer 位置(按 nginx 默认日志格式)。如果里面出现一堆不是你域名的站,那就是被白嫖了。

一、规则怎么写

在站点配置里加一段:

location ~* \.(woff2?|ttf|otf|eot)$ {
    valid_referers none blocked example.com www.example.com *.example.com;
    if ($invalid_referer) {
        add_header Cache-Control "no-store" always;
        return 403;
    }
}

逐段解释:

部分作用
valid_referers列出允许的来源。none = 没有 Referer 头的请求(直接打开、部分客户端);blocked = Referer 被匿名化(如 https 降级或隐私模式);后面是你自己的域名
$invalid_referernginx 内置变量,Referer 不在白名单里时为真
return 403直接拒绝
add_header Cache-Control "no-store" always关键:防止这个 403 被 CDN 或浏览器缓存住,否则以后正常访客也可能一直拿 403

二、两个必须注意的点

① 一定要带 none

不带 none 的话,那些不发送 Referer 的合法请求(比如用户直接粘贴字体地址、某些 APP 内置浏览器)也会被 403。这不是矫情——排障时你自己用 curl 测就会踩到。

② 只放行自己的域名,别图省事写通配

valid_referers * 等于没做。同理,如果你的站点有多个域名(主域 + 别名域),要逐个列全,漏了会出现"某个域名访问时样式错乱"这种很难查的问题。

三、验证方法

一条命令两个方向:

# 期望 403:带一个别人的 Referer
curl -s -o /dev/null -w '%{http_code}\n' 
  -e 'https://别人的站.com/' https://www.example.com/wp-content/themes/x/fonts/a.woff2

# 期望 200:不带 Referer(浏览器直接访问的情形)
curl -s -o /dev/null -w '%{http_code}\n' 
  https://www.example.com/wp-content/themes/x/fonts/a.woff2

如果第二条也返回 403,说明白名单漏了 none。

字体文件路径因主题而异,从首页源码里找 .woff2 的实际地址来测最准。

四、别忘了一件事:规则顺序

nginx 的 location 匹配有优先级:= 精确 → ^~ 前缀 → 正则 ~(按配置文件里的出现顺序) → 普通前缀。所以这段正则 location 如果和别的正则 location 冲突,谁在前面谁生效。

最稳妥的做法:把这类安全/防盗规则放在一个独立文件里,用前缀(比如 00-)保证它排在最前,而不是直接塞进 vhost 主体——这样面板重新生成 vhost 时也不会被覆盖。

五、回滚

删掉这段 location(或整个独立配置文件),nginx -t && nginx -s reload 即可。正常情况下页面不会有任何变化——只有原本被盗用的流量会被切断。


一句话总结:字体防盗链和图片防盗链一个道理,但更容易被忽略,因为字体往往在主题目录里、不在你的图床清单上。写规则时记住两点:必须带 none,403 必须带 no-store。