Nginx防盗链首选valid_referers + $invalid_referer + if + return 403,Apache需mod_rewrite;两者仅防懒盗链,防伪造需Nginx secure_link或Apache Lua模块,且valid_referers必须置于location块内按资源类型配置,none和blocked不可或缺。

直接结论:Nginx 防盗链首选 valid_referers + $invalid_referer + if + return 403,开箱即用、无需编译;Apache 则靠 RewriteCond + RewriteRule 实现,但必须启用 mod_rewrite。两者都只防“懒盗链”,无法抵御 Referer 伪造——真要防伪造,Nginx 得上 secure_link 模块(多数发行版默认不带),Apache 得配合 Lua 或自定义鉴权模块。
为什么 valid_referers 必须写在 location 块里
valid_referers 只在 server 或 location 块中生效,写在 http 块顶层完全无效——Nginx 启动时不报错,但所有防盗链逻辑形同虚设。
- 常见错误:把
valid_referers none blocked example.com;放在http { }里,结果所有.jpg请求都放行 - 更隐蔽的坑:放在
location /下,而非专门匹配资源的location ~* \.(jpg|png|pdf)$,导致 HTML 页面也受 Referer 限制,内链跳转失败 - 正确做法:按资源类型切分
location,例如图片、视频、PDF 各走各的规则,避免误伤或漏防
none 和 blocked 不是“宽松选项”,而是真实访问必需
none 允许请求头中压根没有 Referer 字段的请求,比如用户直接在浏览器地址栏输入图片 URL、点击收藏夹、PWA 离线加载;blocked 允许 Referer 被中间代理/企业网关/浏览器扩展清空或改写成 localhost、about:blank、127.0.0.1 的请求。
- 漏掉
none:移动端 WebView 加载图片失败、微信内嵌页白屏 - 漏掉
blocked:内网调试时图片全 403、某些银行/政企环境访问异常 - 域名匹配不看协议和路径:写
example.com不等于www.example.com,子域名必须显式写*.example.com
return 403 比 rewrite 到默认图更可靠
用 return 403 是最干净的选择,HTTP 语义明确,日志可直接过滤盗链流量;而 rewrite ^/ /static/forbidden.png break; 会埋下多个隐患:
- 原始请求带查询参数(如
/img/logo.png?v=2)时,rewrite默认不携带参数,导致默认图 URL 错误、返回 404 - 状态码仍是 200,监控系统无法区分“正常访问”和“盗链伪装”
- 若
/static/forbidden.png也在同一location下,可能触发二次匹配,形成隐式循环(除非加last或用精确匹配location = /static/forbidden.png排除) - 真要返回图片,应改用
error_page 403 =200 /static/forbidden.png;,既保持 403 语义,又统一返回内容
secure_link 模块不是“升级版 valid_referers”,而是另一套机制
secure_link 不依赖 Referer,而是靠 URL 中携带签名(如 /file.zip?md5=abc123&expires=1743728400),服务端用密钥重新计算哈希并校验时间戳。但它有硬性前提:
- 编译 Nginx 时必须启用
--with-http_secure_link_module,Ubuntu/Debian 官方包、CentOS Stream 8+ 默认不含 - 配置后需手动计算签名,前端调用必须带完整参数,不适合纯静态页面直出场景
- 一旦密钥泄露或时间戳偏差过大,整个链接体系失效,运维成本远高于
valid_referers
真正容易被忽略的是:你写的 valid_referers 规则是否覆盖了所有合法来源?比如合作方用了 CDN 中转、第三方平台嵌入了 iframe、或者你自己开了 PWA 离线缓存——这些场景下的 Referer 行为都和普通浏览器不同,得实测验证,不能只靠“理论上应该能过”。


















