Nginx防盗链最实用方式是valid_referers + $invalid_referer + if + return 403,仅防懒盗链;防伪造需用secure_link模块,但多数发行版默认不带,且须编译启用。

直接结论:Nginx 防盗链最实用、最易落地的方式是 valid_referers + $invalid_referer + if + return 403,但必须清楚它只防“懒盗链”,不防 Referer 伪造;真要防伪造,得上 secure_link 模块,且多数发行版默认不带。
valid_referers 放在哪才生效?
它只在 server 或 location 块里有效,不能写在 http 块顶层——写了也白写,Nginx 启动时不会报错,但完全不生效。
常见错误配置:
-
valid_referers写在http { ... }里,结果所有图片请求都放行 - 想全局控制却放在错误的
location下(比如放在location /而非专门匹配静态资源的location ~* \.(jpg|png|webp)$)
正确做法:专图专用,把防盗链逻辑锁死在资源路径上,例如:
location ~* \.(jpg|jpeg|png|gif|webp|svg|mp4|pdf)$ {
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
expires 7d;
}none 和 blocked 到底允许什么请求?
这两个参数不是“随便放行”,而是对应真实访问场景:
-
none:HTTP 请求头里压根没Referer字段,比如用户直接在浏览器地址栏输入图片 URL、点击收藏夹、或某些 PWA 离线加载 -
blocked:Referer字段存在,但值被中间代理/防火墙/浏览器扩展清空或改写成非http:///https://开头的字符串(如localhost、127.0.0.1、about:blank)
漏掉它们,会导致大量合法访问(尤其是内网调试、移动端 WebView、企业网关环境)被误拦。别为了“看起来更严”就删掉 none blocked。
return 403 还是 rewrite 到默认图?
用 return 403 是更干净的选择,原因很实际:
-
rewrite ^/ /static/forbidden.png break;不带原始查询参数(如?v=2),导致默认图 URL 错误、404 - 状态码仍是
200,日志里全是成功响应,根本分不清哪些是盗链流量 - 如果默认图也在同一
location下,可能触发二次匹配,形成隐式循环(除非加last或精确location = /static/forbidden.png排除)
真要返回图片,用 error_page 更稳妥:
error_page 403 =200 /static/forbidden.png;
这样既保持语义正确(拦截就是 403),又让客户端看到一张图,还不污染日志和匹配逻辑。
secure_link 模块启用前必须确认三件事
它能防伪造、带时效、可签名单链接,但代价高:
- Ubuntu/Debian 官方源的
nginx-full默认不含--with-http_secure_link_module;CentOS Stream 8+ 也不带;必须自己编译或换 OpenResty - URL 必须带签名参数,如
/file.zip?md5=abc123&expires=1743728400,前端生成逻辑复杂,不适合纯静态站点 -
secure_link_md5的密钥和拼接规则(如secret + $uri + $arg_expires)一旦写错,所有链接立即失效,排查困难
简单站点别硬上 secure_link;只有当你要发限时下载链接、做会员资源分发、或 CDN 回源鉴权时,才值得投入这个成本。


















