防盗链必须在缓存决策前执行:valid_referers与$invalid_referer判断须置于location块最开头,早于expires、Cache-Control及proxy_cache指令;403响应需设no-store等头禁止缓存,并用error_page而非rewrite处理提示。

静态资源被外部网站盗链后,如果又被其 CDN 或浏览器大量缓存,会导致你失去对资源访问的控制权——即使后续更新防盗链规则或下架文件,盗链方仍能长期通过缓存提供服务。要避免这种情况,关键不是“禁止缓存”,而是让缓存行为与防盗链逻辑协同:合法请求可缓存,非法请求绝不缓存,且缓存响应本身不能掩盖防盗链状态。
防盗链必须在缓存决策前完成
valid_referers 和 $invalid_referer 判断必须放在 location 块最开头,早于任何 expires、add_header Cache-Control 或 proxy_cache 指令。否则 Nginx 可能先决定缓存响应,再执行防盗链检查,结果把 403 错误也缓存下来,造成合法 Referer 后续请求持续被拒。
- ✅ 正确顺序:valid_referers → if ($invalid_referer) { return 403; } → 缓存指令(如 expires)
- ❌ 错误写法:先写 expires 1y,再写防盗链判断——此时 403 可能被缓存数小时甚至更久
- 若使用 proxy_cache,必须显式排除 403 状态码:proxy_cache_valid 200 304 1y; proxy_cache_valid 403 1s;
对非法请求返回不可缓存的 403
当防盗链拦截发生时,返回的 403 响应必须明确禁止缓存,防止中间代理或浏览器保存错误状态。
- 在防盗链 block 内添加:add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
- 避免用 rewrite 跳转到提示图(如 return 302 /forbidden.jpg),因为跳转后新请求可能返回 200 并被缓存,掩盖真实拦截意图
- 若需展示友好提示,改用 error_page 403 /403.html,并确保 /403.html 本身也带 no-cache 头
对合法资源启用带校验的长缓存
允许缓存,但不等于放任缓存。合法来源的资源可以设较长缓存时间(如 1 年),前提是配合强校验机制,确保内容更新后能立即失效。
- 启用 ETag 或 Last-Modified:Nginx 默认对静态文件生成 ETag,无需额外配置;确认未关闭 etag on;
- 配合版本化路径:/static/app.js?v=2.3.1 或 /static/app.abc123.js,让 URL 变化即触发新缓存
- 避免仅靠 expires 指令:单独设 expires 1y 不足以应对内容更新,必须有 URL 或校验头配合
按资源风险分层设置缓存策略
不是所有静态资源都适合长缓存。高盗链风险资源(如封面图、下载 PDF)应缩短缓存周期或禁用公共缓存,降低被滥用后的影响时长。
- 图片/视频/字体等易盗链资源:expires 1h 或 add_header Cache-Control "public, max-age=3600, immutable";
- CSS/JS 等前端逻辑资源:推荐版本哈希路径 + expires 1y,因更新必改 URL
- 公开 API 静态响应(如 /api/status.json):不防盗链,但加 limit_req + Cache-Control "no-cache, must-revalidate"











