假设你的服务器上有一张精美的图片 http://your-site.com/images/photo.jpg,你的网站首页 index.html 中引用了它:
|
1 2 |
<!-- 你的网站正常引用 --> <img src="http://your-site.com/images/photo.jpg" /> |
这时候浏览器发起请求时,请求头中会携带:
|
1 |
Referer: http://your-site.com/index.html |
但如果其他网站直接在你的图片链接上做文章:
|
1 2 |
<!-- 别的网站直接引用你的图片 --> <img src="http://your-site.com/images/photo.jpg" /> |
请求头变成:
|
1 |
Referer: http://other-site.com/index.html |
这就是盗链 — 别的网站不经过你的允许,直接引用你的静态资源(图片、CSS、JS、视频等),消耗你的服务器带宽和资源。
| 需求 | 说明 |
|---|---|
| 阻止外部网站引用 | 非授权的网站无法使用你的静态资源 |
| 允许正常访问 | 你自己的网站可以正常加载资源 |
| 可允许直接访问 | 可选:控制是否允许用户在浏览器地址栏直接打开资源链接 |
| 保护带宽 | 避免资源被非法盗用,节省服务器流量成本 |
当浏览器访问一个页面时,会在请求头中带上 Referer 字段,表示"我是从哪里跳转过来的":
| 场景 | Referer 值 |
|---|---|
| 从首页访问图片 | http://your-site.com/index.html |
| 从其他网站访问你的图片 | http://other-site.com/xxx.html |
| 直接输入 URL 访问图片 | 无 Referer(为空) |
Nginx 通过 valid_referers 指令,检查请求头中的 Referer 字段是否在允许的白名单中:
|
1 |
valid_referers none | blocked | server_names | string ...; |
| 参数 | 含义 |
|---|---|
| none | 允许没有 Referer 的请求(例如直接在浏览器地址栏输入 URL 访问) |
| blocked | 允许 Referer 存在但被代理/防火墙隐藏的请求(Referer 值不以 http:// 或 https:// 开头) |
| server_names | 允许 Referer 为当前 server 块中配置的域名 |
| 字符串 | 自定义允许的域名,支持 * 通配符(如 *.example.com) |
| ~正则 | 使用正则表达式匹配 Referer(如 ~\.mysite\.com) |
|
1 2 3 4 5 6 7 |
location ~ \.(jpg|jpeg|png|gif|webp)$ { valid_referers none blocked example.com *.example.com; if ($invalid_referer) { # 如果 Referer 不在白名单,$invalid_referer 会被设为 1 return 403; } } |
$invalid_referer 是 Nginx 的内置变量:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
server { listen 80; server_name www.example.com; location ~ \.(jpg|jpeg|png|gif|bmp|webp)$ { # 允许:无 Referer、被屏蔽的 Referer、本站域名 valid_referers none blocked www.example.com example.com; if ($invalid_referer) { return 403; # 盗链者返回 403 禁止访问 } root /var/www/images; } } |
效果:
不想返回 403 显得太生硬?可以返回一张"禁止盗链"的提示图片:
|
1 2 3 4 5 6 7 8 9 |
location ~ \.(jpg|jpeg|png|gif|webp)$ { valid_referers none blocked www.example.com example.com; if ($invalid_referer) { rewrite ^/images/(.*)$ /images/forbidden.png redirect; # 或者直接重定向到一个警告图片 # rewrite ^ /images/forbidden.png break; } root /var/www/images; } |
这样盗链者看到的是一张提示图片,而不是丑陋的 403 页面。
|
1 2 3 4 5 6 7 8 9 10 11 |
location ~ \.(jpg|jpeg|png|gif)$ { valid_referers none blocked example.com *.example.com # 所有子域名 *.google.com # 允许谷歌搜索引用 ~\.mysite\.com; # 正则匹配 if ($invalid_referer) { return 403; } root /var/www/html; } |
有些场景下,你希望资源只能通过你的网站页面引用,不允许直接在浏览器地址栏打开:
|
1 2 3 4 5 6 7 8 |
location ~ \.(jpg|jpeg|png|gif)$ { # 注意:不写 none,意味着没有 Referer 的请求也会被拒绝 valid_referers blocked www.example.com example.com; if ($invalid_referer) { return 403; } root /var/www/html; } |
区别:
不需要所有图片都防盗链,只针对某个敏感目录:
|
1 2 3 4 5 6 7 |
location /assets/protected/ { valid_referers none blocked www.example.com; if ($invalid_referer) { return 403; } alias /var/www/html/assets/protected/; } |
|
1 2 3 4 5 6 7 |
location ~ \.(jpg|jpeg|png|gif|webp)$ { valid_referers none blocked example.com *.example.com; if ($invalid_referer) { # 盗链时重写到一个警告图片 rewrite ^/images/(.*)$ /images/forbidden.png redirect; } } |
这里 redirect 返回 302 临时重定向,浏览器地址栏会变化。如果不想暴露,也可以用 break 内部重写。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
location ~ \.(jpg|jpeg|png|gif)$ { valid_referers none blocked example.com; if ($invalid_referer) { return 403; } root /var/www/html; } # 自定义 403 页面 error_page 403 /403.html; location = /403.html { root /var/www/html; } |
| 局限性 | 说明 |
|---|---|
| Referer 可伪造 | HTTP 请求头可以被开发者工具或程序修改,不能做到 100% 防护 |
| 部分浏览器/代理会屏蔽 Referer | 隐私模式下 Referer 可能为空,误伤正常用户 |
| HTTPS → HTTP 不传 Referer | 从 HTTPS 网站跳转到 HTTP 资源时,浏览器不会发送 Referer |
| 关键点 | 要点 |
|---|---|
| 原理 | 检查 HTTP 请求头的 Referer 字段是否在白名单中 |
| 指令 | valid_referers + $invalid_referer 变量 |
| 常用参数 | none(允许空 Referer)、blocked、域名、* 通配符 |
| 处理方式 | return 403 拒绝,或 rewrite 替换为警告图片 |
| 局限性 | Referer 可伪造,但不妨碍它挡住 90% 的随意盗链 |
防盗链配置简单但效果显著,是 Nginx 静态资源服务器标配功能之一。虽然不能做到绝对安全,但已经能挡住绝大多数盗链行为,有效保护你的带宽资源。