跳到主要内容

防盗链、跨域与 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 代理一个源站时:

  1. 用户访问 你的域名
  2. CDN 去源站取内容
  3. CDN 默认带的 Host你的域名
  4. 源站一看:我这没有 你的域名 这个站 → 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. 配置保存后等 1-2 分钟
  2. 在你的网站里加载 OSS 图片 → 正常显示 ✅
  3. 在其他域名(如本地 HTML 文件从 file://)用 <img> 引用 → 应该 403 ✅
  4. 直接在浏览器地址栏打开 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)*
允许 MethodsGET, HEAD
允许 Headers*
缓存时间86400(秒)

安全版(只允许自己的域名 JS 读取,推荐以后使用):

配置项
来源(Origin)逐行填入域名(见下方)
允许 MethodsGET, 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
  • 百度服务器看到陌生 Host403

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
你自己在博客里正常使用✅ 允许白名单

适合接着读