nginx防盗链最实用方式是valid_referers+if+return 403,但仅防懒盗链;真防伪造需secure_link模块,而多数发行版默认不带,且valid_referers必须置于location块内才生效。

直接结论:Nginx 防盗链最实用、最易落地的方式是 valid_referers + $invalid_referer + if + return 403,但它只防“懒盗链”,不防 Referer 伪造;真要防伪造,得用 secure_link 模块,而多数发行版默认不带。
valid_referers 必须放在 location 块里才生效
很多人把 valid_referers 写在 http { } 或 server { } 顶层,结果配置完全不生效——Nginx 启动时不报错,但所有图片请求照常放行。它只在 location 块内起作用,且必须和你要保护的资源路径精确匹配。
- 错误写法:
http { valid_referers none blocked example.com; }→ 白配 - 正确写法:专图专用,例如只对静态资源生效:
location ~* \.(jpg|png|webp|mp4|pdf)$ { valid_referers none blocked example.com *.example.com; ... } - 别图省事塞进
location /:那样会干扰 API、HTML、JS 等非静态请求,甚至导致登录跳转失败
none 和 blocked 不是“宽松选项”,而是真实访问必需项
none 和 blocked 对应的是两类常见合法流量,删掉它们等于主动拦截用户:
Linux AI 服务器 — 将 Linux 服务器转换为本地 AI 推理集群。使用 systemd、NVIDIA CUDA 实现无头 Linux AI,零 GUI 开销。
-
none:浏览器地址栏直接输入图片 URL、点击收藏夹、PWA 离线加载、部分 WebView 场景——这些请求压根不带Referer头 -
blocked:企业网关、某些防火墙、浏览器扩展(如隐私模式插件)、本地调试(localhost、127.0.0.1)会清空或篡改Referer,使其变成非http:///https://开头的值 - 漏掉它们,移动端 WebView 加载失败、内网测试 403、员工访问内部系统图片报错——都是高频误拦场景
return 403 比 rewrite 到默认图更干净
用 if ($invalid_referer) { return 403; } 是推荐做法;换成 rewrite ^/ /static/forbidden.png break; 容易埋坑:
- 原始请求带查询参数(如
/img/logo.png?v=2),rewrite默认不携带参数,导致默认图加载失败 - 状态码仍是 200,日志里全是成功响应,监控无法识别盗链流量,CDN 也难做统计
- 如果默认图也在同一
location下,可能触发循环匹配(除非加last或用error_page显式隔离) - 真要返回图片,用
error_page 403 =200 /static/forbidden.png;更稳妥:语义仍是 403,但响应体是图片,HTTP 层逻辑清晰
secure_link 才能防伪造,但得自己编译启用
当业务需要真正防伪造、有时效性、或按用户/会话生成单次有效链接时,secure_link 是唯一靠谱方案。但它不是开箱即用的:
- Ubuntu/Debian 官方包、CentOS Stream 8+ 默认不含
ngx_http_secure_link_module,nginx -V输出里看不到--with-http_secure_link_module就说明没启用 - 必须源码编译时加
--with-http_secure_link_module,或换 OpenResty - 配置依赖 URL 参数(如
?md5=abc123&expires=1743728400),服务端用密钥重算哈希并校验时间戳,逻辑比valid_referers重得多 - 别在没确认模块可用前就写
secure_link $arg_md5,$arg_expires;——Nginx 会直接启动失败,报unknown directive "secure_link"
复杂点在于:valid_referers 看似简单,但 none 和 blocked 的实际覆盖范围,远比文档描述的“宽松”更关键;而 secure_link 虽强,但启用门槛和维护成本,常常被低估。










