Nginx配置防盗链
我们在日常浏览网页时,偶尔会遇到图片裂开的情况;若试着在新标签页中单独打开该图片的链接,浏览器可能会弹出一个 403 Forbidden(禁止访问) 的错误提示。这通常意味着:当前网页并未在自己的服务器上托管这张图片,而是直接在外链中引用了其他站点的资源。被引用的源站为了保护自身的服务器资源与出口带宽,通过 Nginx 识别出了此类未经授权的外部请求,并主动返回了 403 拒绝访问。
这种用来防范外部网站直接引用本站静态资源的保护机制,就是我们常说的 Nginx 防盗链(Anti-Hotlinking)。
1. 原理浅析:HTTP Referer#
浏览器在加载网页内的图片、音视频等资源时,遵循 HTTP 协议规范,通常会在请求头中自动附带一个 Referer 字段,用于告知服务端“这个资源请求是从哪个网页发起的”。
例如,当用户在第三方网站 https://other-site.com 浏览时,页面加载了本站的图片,浏览器向本站发出的请求大致如下:
1GET /logo.png HTTP/1.1
2Host: your-domain.com
3Referer: https://other-site.com/page.html
Nginx 正是通过内置的 ngx_http_referer_module 模块,提取请求头中的 Referer 并与我们设定的白名单进行比对,从而判断请求来源是否合规。
不过,Referer 可能因为浏览器隐私策略、Referrer-Policy、代理或浏览器扩展而缺失,也可以被客户端伪造。因此,Referer 防盗链适合做粗粒度限制,不应作为身份认证或强安全边界。
- 注意事项:不要把 Referer 白名单当成搜索引擎认证。因为搜索引擎抓取图片时不一定会携带 Referer。本例保留 none,已经允许无来源头的正常请求;如果确实遇到特定爬虫无法访问,应先结合实际请求日志排查,再按业务需要增加白名单。仅凭 Referer 域名无法可靠证明请求来自搜索引擎,也不能单独保证 SEO 效果。
2. 解决方案#
为了兼顾系统安全与业务体验,针对未经许可的外部引用,Nginx 通常有两种主流的配置策略:
2.1 直接返回 403 状态码(更轻量、最省带宽)#
发现请求来源不合规时立即切断响应,不给对方传输多余数据,这是大多数生产环境的首选方案。
1# 图片与多媒体资源防盗链配置
2location ~* \.(gif|jpg|jpeg|png|bmp|webp|avif|svg|mp4|mov)$ {
3 # 仅对 URL 带版本指纹的静态资源使用 immutable
4 # 使用单个 Cache-Control 头,避免与 expires 重复设置缓存策略
5 expires off;
6 add_header Cache-Control "public, max-age=2592000, immutable";
7
8 # 流量很大时可关闭;如需审计或排查盗链,请保留日志或改用条件日志
9 access_log off;
10
11 # 合法 Referer 白名单配置:
12 # - none: 允许直接在浏览器地址栏打开或从书签访问(无 Referer)
13 # - server_names: 自动匹配本 server 声明的所有合法域名
14 # - *.example.com: 放行 example.com 下的子域名
15 valid_referers none server_names *.example.com;
16
17 # 非法来源直接返回 403 阻断
18 if ($invalid_referer) {
19 return 403;
20 }
21}
核心参数说明:
- none:匹配请求头中完全没有 Referer 的情况。比如访客在浏览器地址栏直接粘贴图片链接打开、或从书签访问时,都不会携带 Referer。保留此项能确保正规用户的直接访问不受阻碍。
- blocked:匹配存在 Referer,但值不以 http:// 或 https:// 开头的请求。这类值可能来自代理,也可能是客户端伪造;只有明确需要兼容此类代理时才建议加入。
- server_names:直接复用当前 server 块中配置的域名列表,避免重复硬编码。
- 通配符域名:*.example.com 表示放行 example.com 下的子域名。请只填写实际需要信任的域名,避免使用过宽的通配符。
2.2 返回友好的警示占位图(内部改写)#
如果业务上希望给外部引用方一个明确的视觉反馈(例如在对方页面展示一张写着“资源来自 xxx,请前往原站查看”的说明图),可以通过内部改写来实现。这样浏览器地址栏仍然保持原始图片 URL,但响应内容替换为占位图。
1location ~* \.(gif|jpg|jpeg|png|webp)$ {
2 # 配置合法 Referer 白名单
3 valid_referers none server_names *.example.com;
4
5 # 非法来源通过内部改写重写至占位图
6 if ($invalid_referer) {
7 # 内部改写后重新匹配 location,让下面的精确 location 接管请求
8 rewrite ^ /static/warning/anti_hotlink.png last;
9 }
10
11 # 原始图片可以按业务需要缓存;不要与占位图共用 no-store 策略
12 expires off;
13 add_header Cache-Control "public, max-age=2592000";
14 access_log off;
15}
16
17# 警告图片单独放行,并禁止缓存,避免占位图被缓存到原始图片 URL
18location = /static/warning/anti_hotlink.png {
19 root /usr/share/nginx/html; # 静态文件根目录
20 expires off; # 禁用 expires 响应头
21 add_header Cache-Control "no-store" always; # 始终禁止客户端与代理缓存
22 access_log off; # 占位图通常关闭独立访问日志
23}
机制说明与注意事项:
- 内部改写机制:这里使用的是内部改写,不会向客户端返回 3xx 重定向状态码。last 会让 Nginx 重新进行 location 匹配,因此请求会进入精确匹配的占位图规则,不会再次执行外层防盗链判断。若改成 break,请求会继续留在当前正则 location 中,下面的精确匹配规则不会被重新执行;若确实需要浏览器发生跳转,应改用 return 302 /static/warning/anti_hotlink.png,并相应使用 curl -L 验证。
- 缓存策略隔离:占位图必须设置 no-store,否则外部请求得到的占位图可能被浏览器或 CDN 按原始图片 URL 缓存,进而影响后续合法访问。
3. 验证与排查#
3.1 语法校验与平滑重载#
配置修改完成后,建议先执行语法校验并平滑重载:
1# 检查语法是否有误
2nginx -t
3
4# 平滑重载生效(不中断现有连接)
5nginx -s reload
3.2 使用 curl 模拟请求验证#
随后可以用 curl 命令快速验证策略是否如预期生效:
1# 方案一:模拟本站正常引用(带合法 Referer,期望返回 200)
2curl -I -e "https://www.example.com" https://www.example.com/logo.png
3
4# 方案一:模拟外部未授权引用(带非法 Referer,期望返回 403)
5curl -I -e "https://unauthorized-domain.com" https://www.example.com/logo.png
6
7# 切换到方案二配置后:模拟外部未授权引用(带非法 Referer,期望返回 200,内容为占位图)
8curl -I -e "https://unauthorized-domain.com" https://www.example.com/logo.png
9
10# 方案一或方案二:模拟地址栏直接打开(无 Referer,期望返回 200)
11curl -I https://www.example.com/logo.png