防盗链、跨域与 Host 校验:Web 资源访问控制详解
当你把图片、CSS、JS 等静态资源放在 OSS 或 CDN 上,有三个机制分别从不同维度控制"谁能访问"和"怎么访问"。
它们名字像、场景交叉、容易混淆,但各管各的事,不能互相替代。
三句话先讲清楚
| 机制 | 防什么 | 谁管 | 一句话 |
|---|---|---|---|
| Host 校验 | 请求的目标域名不对 | 服务器 | "你报的名字是不是我这台机器上有的站" |
| 防盗链(Referer) | 别的网站用 <img> 偷你的图片 | 服务器 / CDN | "你从哪个页面点过来的,是不是自己人" |
| 跨域(CORS) | 别的网站用 JS 读你的资源数据 | 浏览器 | "浏览器允许这个跨站 JS 请求吗" |
Host 请求头
什么是 Host
用户每次访问网站,浏览器都会在 HTTP 请求里带一个 Host 头:
GET / HTTP/1.1
Host: www.baidu.com
一台服务器上可能托管了成百上千个网站,服务器靠 Host 头来区分"你要访问的是哪个站"。
CDN 回源时的 Host 问题
当你用 CDN 代理一个源站时:
- 用户访问
你的域名 - CDN 去源站取内容
- CDN 默认带的
Host是你的域名 - 源站一看:我这没有
你的域名这个站 → 403
解决方法:在 CDN 回源配置里,把回源 Host 设置为 源站域名。这样 CDN 回源时携带的 Host 就是源站自己的域名,源站认得,正常返回。
为什么 GitHub Pages 可以正常回源
GitHub Pages 只用 Host 头做路由——把请求分发到对应 xxx.github.io 的仓库内容。它没有 Host 黑名单,不会因为 Host 是非白名单域名就直接拒绝。只要回源 Host 指向了一个真实存在的 Pages 地址,就能正常响应。
所以直接 CNAME 到 github.io 能访问:GitHub 收到 Host: yourdomain.com 后,通过 SNI 或默认路由仍然能找到你的仓库并返回页面——它不像商业大站那样拦截陌生 Host。
为什么大厂不可以
百度、B 站、淘宝等商业大站不只是校验 Host,而是多层组合防护:
| 防护层 | 能否绕过 | 说明 |
|---|---|---|
| Host 校验 | 可通过回源 Host 配置绕过 | 把回源 Host 设为源站域名即可 |
| IP 封禁 | ❌ 绕不过 | CDN 节点 IP 池早已被标记为机房/代理 |
| TLS 指纹校验 | ❌ 绕不过 | SSL/TLS 握手特征暴露非正常浏览器 |
| 全局风控 | ❌ 绕不过 | 流量模式、频率、Cookie/Session 全被监控 |
所以即使你把源站填 baidu.com、回源 Host 也填 baidu.com,百度依然会 403 拒绝——Host 只是第一关,后面还有三道墙。
哪些网站能被套 CDN / 反向代理
✅ 可以(满足全部条件:Host 仅用于路由不设黑名单、无 IP 封禁、无防盗链风控、纯静态):
- GitHub Pages
- Vercel / Netlify 静态站
- 个人小博客、开源文档站
- 你自己名下的服务器 / 虚拟主机
❌ 不行:
- 百度、阿里、腾讯、网易、B 站、抖音等所有商业大站
- 电商、支付、银行、政府网站
- 带登录 / 会员 / 接口鉴权的任何站点
- 有版权防盗链的视频 / 音乐 / 图片站
防盗链(Referer)
原理
浏览器在请求页面里的图片、CSS、JS 等资源时,会自动带一个 Referer 头,说明"我是从哪个页面点过来的":
GET /logo.png HTTP/1.1
Host: cdn.example.com
Referer: https://www.zhihu.com/question/xxx
服务器检查 Referer 是否在白名单内:
- 在 → 正常返回
- 不在 → 403 Forbidden
防盗链管什么、不管什么
管的:
- 别人在他们网站里用
<img>、<video>、<audio>等标签引用你的资源 - 别人用
<link>或@import引用你的 CSS 文件 - 凡是浏览器会在请求时自动带上
Referer的资源引用,防盗链都能拦截
不管的:
- 别人直接在浏览器地址栏打开图片链接(此时
Referer为空,提交给"空 Referer"设置裁决) - 别人用
curl等命令行工具下载(默认不带Referer,除非手动指定) - 别人从本地 HTML 文件(
file://协议)引用——Referer通常为空或不完整
阿里云 OSS 防盗链配置
白名单 Referer(每行一个,换行分隔):
http://nevergpdzy.cn
https://nevergpdzy.cn
http://*.nevergpdzy.cn
https://*.nevergpdzy.cn
http://nevergpdzy.com
https://nevergpdzy.com
http://*.nevergpdzy.com
https://*.nevergpdzy.com
http://nevergpdzy.github.io
https://nevergpdzy.github.io
http://*.nevergpdzy.github.io
https://*.nevergpdzy.github.io
配置要点:
- 必须同时写
http://和https://,两种协议独立校验 *.域名是通配符,覆盖所有子域名(也覆盖了主域名本身)- 多个域名之间用换行分隔
空 Referer 设置:
- 允许(推荐):浏览器直接输入图片地址、
curl、本地调试都能正常访问 - 不允许:只有从你白名单域名的网页引用才能打开图片;直接复制链接到地址栏会 403
对于个人博客场景,建议保持"允许",不影响你日常使用和调试。
截断 QueryString:一般保持默认"允许"即可,OSS 防盗链只看域名部分,不检查 URL 参数。
验证防盗链是否生效
- 配置保存后等 1-2 分钟
- 在你的网站里加载 OSS 图片 → 正常显示 ✅
- 在其他域名(如本地 HTML 文件从
file://)用<img>引用 → 应该 403 ✅ - 直接在浏览器地址栏打开 OSS 图片链接 → 取决于"空 Referer"设置
跨域(CORS)
原理
跨域是浏览器限制,不是服务器限制。
浏览器安全策略:一个域名的 网页(a.com),用 JS 去请求另一个域名(b.com)的资源时,浏览器会检查 b.com 的响应头里有没有 Access-Control-Allow-Origin。如果没有,浏览器直接拦截,JS 代码收到网络错误。
对于 GET / HEAD 这类简单请求,浏览器直接发请求,然后检查响应头。对于带自定义 Header、PUT / DELETE 等非简单请求,浏览器会先发一个 OPTIONS 预检请求,问服务器"你允不允许这个跨域请求",被允许后才发真正请求。
// 简单请求(如 GET):
a.com 的 JS → fetch('https://b.com/data.json')
→ 浏览器直接发 GET,检查响应头有无 Access-Control-Allow-Origin
→ 没有 → 拦截报错
// 非简单请求(如带自定义 Header):
a.com 的 JS → fetch('https://b.com/data.json', { headers: { 'X-Custom': '1' } })
→ 浏览器先发 OPTIONS 预检:"允许 X-Custom 这个头吗?"
→ 不允许 → 拦截,真正的 GET 根本不会发出
CORS 管什么、不管什么
管的(只有浏览器里的 JS):
fetch()/axios/XMLHttpRequest跨域请求- Canvas
drawImage()读取跨域图片像素(getImageData/toDataURL) - JS 下载、读取、处理跨域文件
不管的:
<img src="...">标签加载图片——浏览器不检查跨域- CSS
background-image——不检查跨域 - 浏览器地址栏直接打开——不检查跨域
curl/ Postman / 服务器间请求——没有浏览器,根本不存在跨域
什么时候需要配置 CORS
必须配置:
- 网站用 JS 加载 / 读取 OSS 上的文件
- Canvas 处理 OSS 图片(加水印、裁剪、取色等)
- 前端预览上传前用 JS 读取图片信息
不需要配置——你只是用 <img> 标签展示图片就完全不需要 CORS。这个场景下 CORS 可以关掉,或者保持 * 也无影响。
阿里云 OSS 跨域配置
宽松版(允许所有域名跨域,适合纯展示场景):
| 配置项 | 值 |
|---|---|
| 来源(Origin) | * |
| 允许 Methods | GET, HEAD |
| 允许 Headers | * |
| 缓存时间 | 86400(秒) |
安全版(只允许自己的域名 JS 读取,推荐以后使用):
| 配置项 | 值 |
|---|---|
| 来源(Origin) | 逐行填入域名(见下方) |
| 允许 Methods | GET, HEAD |
| 允许 Headers | * |
| 缓存时间 | 86400 |
安全版 Origin 列表(每行一个):
http://nevergpdzy.cn
https://nevergpdzy.cn
http://*.nevergpdzy.cn
https://*.nevergpdzy.cn
http://nevergpdzy.com
https://nevergpdzy.com
http://*.nevergpdzy.com
https://*.nevergpdzy.com
http://nevergpdzy.github.io
https://nevergpdzy.github.io
http://*.nevergpdzy.github.io
https://*.nevergpdzy.github.io
注意:CORS 的
*通配符写法在阿里云 OSS 中是否支持取决于具体产品版本,如果*.nevergpdzy.cn不生效,需要逐条写主子域名。
CORS 和防盗链的分工
| 场景 | 走防盗链 | 走 CORS |
|---|---|---|
<img> 标签展示图片 | ✅ | ❌ 不触发 |
CSS background-image | ✅ | ❌ 不触发 |
JS fetch 读取资源 | ❌ 不触发 | ✅ |
| Canvas 读像素 | ❌ 不触发 | ✅ |
| 浏览器直接打开链接 | 空 Referer 决定 | ❌ 不触发 |
curl 下载 | ❌ 不触发 | ❌ 不触发 |
CNAME 直连 vs CDN 反向代理
这是另一个容易混淆的概念。
DNS CNAME:只改 IP,不改 HTTP
你配了一条 yourdomain.com CNAME → baidu.com:
- DNS 层面:
yourdomain.com被解析到百度的 IP,TCP 能连上 - HTTP 层面:浏览器发的
Host头仍然是yourdomain.com - 百度服务器看到陌生
Host→ 403
CNAME 只是"把路指过去",但"你报的名字(Host)"没变。GitHub Pages 能行只因它不查名字。
CDN 反向代理:可以改 Host
CDN 在转发请求时可以强制修改 Host 头:
- 用户 → CDN:
Host: yourdomain.com - CDN → 源站:
Host: baidu.com(改过了)
所以 CDN 反向代理能绕过 Host 校验——但这只是第一关,大厂还有 IP 黑名单等后续防护。
三者联合配置:最佳实践
以个人博客场景为例(GitHub Pages + 阿里云 OSS 图床):
防盗链(核心防线)
阻止别人在自己网站里用 <img> 偷你的图片。白名单只放你自己的域名,空 Referer 保持"允许"以方便调试。
跨域(按需开启)
如果只是 <img> 展示图片,CORS 不需要配置;如果未来有 JS 处理图片的需求,再按安全版白名单配置。
Host 与回源
CDN 回源 Host 必须匹配源站域名,不能乱填。GitHub Pages 兼容任意 Host,所以这一步配置轻松。
最终安全效果
| 攻击场景 | 是否被阻止 | 靠谁 |
|---|---|---|
别人网站 <img> 引用你的 OSS 图片 | ✅ 阻止 | 防盗链 |
| 别人用 JS 读取你的图片数据 | ✅ 阻止(安全版 CORS) | 跨域 |
| 别人用 CDN 套你的网站做镜像 | ✅ 除非源站是 GitHub Pages 类 | 你要自己防护 |
| 你自己用 curl / 浏览器打开 | ✅ 允许 | 空 Referer |
| 你自己在博客里正常使用 | ✅ 允许 | 白名单 |
适合接着读
- 把这些机制落到 CDN + GitHub Pages 的实操:阿里云 CDN 加速 GitHub Pages 完整指南
- OSS 静态托管的完整方案:用阿里云 OSS 托管静态网站
- CDN 基础概念:CDN 原理与应用