- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
跨域(Cross-Origin)请求是浏览器安全模型的基石,而Origin与Referer是两个极易混淆的 HTTP 请求头:Referer携带完整页面 URL,信息量显然更大,那为什么浏览器还要额外发明并强制保证Origin?本篇以现代 JavaScript 教程中「Fetch: Cross-Origin Requests」的配套问答为骨架,结合仓库中 fetch 基础章节 与 fetch 选项章节 的源码级文档,系统梳理Referer的不可靠性、Origin的设计初衷,以及浏览器作为可信中介在 CORS 中如何利用Origin完成跨域安全校验。读完你将彻底理解两个头的职责边界,并掌握用referrer/referrerPolicy控制Referer的实战方法。
问题背景:请求头里同时出现的两个「来源」
当从http://javascript.info/some/url页面发起对http://google.com的fetch请求时,浏览器发送的 HTTP 请求头大致长这样:
Accept: */* Accept-Charset: utf-8 Accept-Encoding: gzip,deflate,sdch Connection: keep-alive Host: google.com Origin: http://javascript.info Referer: http://javascript.info/some/url如你所见,Referer和Origin同时出现。这里自然引出两个问题:
- 既然
Referer包含了更完整的信息(完整 URL 带路径),为什么还需要Origin? - 是否存在
Referer或Origin缺失、甚至不正确的可能?
核心解答:因为 Referer 不可靠,所以需要 Origin
答案的第一层很简单:我们需要Origin,因为Referer有时会缺席。原文档给出的结论可以用四个事实支撑:
- HTTPS→HTTP 场景下
Referer直接消失:当从 HTTPS 页面(更安全的协议)fetch一个 HTTP 页面(较不安全的协议)时,浏览器会省略Referer。这是「从更安全的环境访问较不安全环境」时的隐私保护策略,而Origin依然照常发送。 - Content Security Policy(内容安全策略)可能禁止发送
Referer:站点可以通过 CSP 指令禁止请求携带Referer头,此时它自然缺席。 fetch本身提供了关闭甚至篡改Referer的选项:如后文所述,referrer: ""可让fetch完全不发送Referer,referrer选项甚至允许在同源范围内修改它的值。- 按规范,
Referer本来就是可选 HTTP 头:RFC 规范中Referer是 optional 的,任何实现都可以选择不发送。
正因为Referer不可靠,Origin才被发明出来——对于跨域请求,浏览器保证发送正确的Origin。换句话说:Origin是浏览器层面的强保证,Referer只是「通常存在、但别指望它」的辅助信息。
纵深一:Origin 与 Referer 的本质区别
对比两个头可以更清楚地看到设计分工:
| 维度 | Origin | Referer |
|---|---|---|
| 携带内容 | 仅 origin(协议 + 域名 + 端口),不含路径 | 完整页面 URL(含路径与查询参数) |
| 信息量 | 少(更克制) | 多(更暴露) |
| 规范地位 | 跨域请求由浏览器强制附加 | 可选头,可能被省略 |
| 浏览器保证 | 跨域请求保证正确 | 不保证(可能缺失、可能被策略禁止) |
| 可否被脚本修改 | 禁止(forbidden header) | 可通过referrer/referrerPolicy影响 |
Origin的「少即是多」并非缺陷:它只暴露「来自哪个站点」这一最小必要信息,避免把内部路径泄漏给第三方服务器。而信息量更大的Referer因为各种原因不可靠,无法承担安全校验的重任——服务器不能基于一个可能缺失、可能被篡改的头做信任决策。
纵深二:浏览器是 CORS 的可信中介
要理解Origin的「保证」从何而来,需要回到 CORS 主章节 的机制描述。只要请求是跨域的,浏览器总是为它附加Origin头,其值恰好是发起页面自身的 origin(domain/protocol/port),不含路径:
GET /request Host: anywhere.com Origin: https://javascript.info ...在跨域请求的整个生命周期中,浏览器扮演着「可信中介」的双重角色:
- 发送侧保证:确保跨域请求带上正确的
Origin头,脚本无法伪造; - 接收侧检查:检查响应中是否存在匹配的
Access-Control-Allow-Origin头(允许的具体 origin 或*),若存在则把响应交给 JavaScript,否则报错。
正因为浏览器是唯一能「保证 Origin 正确」的一方,服务器端 CORS 校验才得以建立在Origin之上。这一完整交互流程可以直观地用主章节的示意图说明:
纵深三:为什么脚本无法伪造 Origin
另一个支撑Origin可靠性的细节来自 fetch 请求头章节:Fetch 规范定义了一批forbidden HTTP headers(禁止请求头),脚本无法通过headers选项设置,其中包括:
Accept-Charset、Accept-EncodingAccess-Control-Request-Headers、Access-Control-Request-MethodConnection、Content-Length、Cookie、Cookie2、Date、DNTExpect、Host、Keep-AliveOriginRefererTE、Trailer、Transfer-Encoding、Upgrade、ViaProxy-*、Sec-*等
注意Origin和Referer都在禁止列表中:这些头「确保正确且安全的 HTTP」,因此由浏览器独占控制。这从实现层面解释了为什么浏览器能「保证」Origin正确——脚本根本没有途径覆盖它。而Referer虽然同样禁止脚本直接设置,却可以缺失或被 CSP 拦截,这正是它与Origin可靠性差异的根源。
实战:用 referrer / referrerPolicy 控制 Referer
理解了「为什么需要 Origin」,再看 fetch-api 的 referrer 章节 中管理Referer的两个选项,思路就清晰了——既然Referer本就不可靠,网站完全可以主动决定它的行为。
referrer选项:精确控制单个请求的 Referer
- 设为空字符串
"":完全不发送Referer头:
fetch('/page', { referrer: "" // 不发送 Referer 头 });- 设为当前 origin 内的任意 URL:替换
Referer的值(注意:仅限当前 origin 内,跨 origin 无法设置):
fetch('/page', { // 假设当前页面位于 https://javascript.info // 可以设置任意 Referer,但仅限当前 origin 内 referrer: "https://javascript.info/anotherpage" });referrerPolicy选项:按请求类型设定通用规则
与精确设置单个值不同,referrerPolicy告诉浏览器对不同类型请求采用何种策略。请求分为三类:同源请求、跨源请求、HTTPS→HTTP(从安全协议到不安全协议)请求。可用值与效果如下:
| 值 | 同源请求 | 跨源请求 | HTTPS→HTTP |
|---|---|---|---|
"no-referrer" | 不发送 | 不发送 | 不发送 |
"no-referrer-when-downgrade" | 完整 | 完整 | 不发送 |
"origin" | 仅 origin | 仅 origin | 仅 origin |
"origin-when-cross-origin" | 完整 | 仅 origin | 仅 origin |
"same-origin" | 完整 | 不发送 | 不发送 |
"strict-origin" | 仅 origin | 仅 origin | 不发送 |
"strict-origin-when-cross-origin"(默认) | 完整 | 仅 origin | 不发送 |
"unsafe-url" | 完整 | 完整 | 完整 |
典型场景:某个后台管理区存在不希望外部站点得知的 URL 结构,默认策略下跨域请求会把完整 URL 放进Referer(如Referer: https://javascript.info/admin/secret/paths)。此时可用:
fetch('https://another.com/page', { // ... referrerPolicy: "origin-when-cross-origin" // Referer 只保留 origin 部分 });这样第三方站点只能看到https://javascript.info,看不到路径。注意该策略是全局性的——不仅作用于fetch,也可通过Referrer-PolicyHTTP 响应头为整页设置默认策略,或通过<a rel="noreferrer">逐链接控制。
总结
Origin的存在是为了弥补Referer的不可靠:Referer是可选头,可能因 HTTPS→HTTP 降级、CSP 策略或fetch的referrer选项而缺失,规范上也不强制其存在。- 跨域请求的
Origin由浏览器保证正确:它只携带 origin 三要素(协议/域名/端口),且属于 forbidden header,脚本无法伪造;浏览器作为可信中介,既负责附加正确的Origin,又负责核对响应中的Access-Control-Allow-Origin决定是否放行。 - 服务器应基于
Origin而非Referer做跨域信任决策:前者是强保证,后者只是「通常存在」的弱信息。 - 想主动管理
Referer,可在fetch中使用referrer: ""移除它、referrer同源替换它,或用referrerPolicy按请求类型设定规则——这些实践均可在本仓库的 CORS 主章节、fetch 请求头章节 与 fetch-api 章节 中逐一验证。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
Windows 1 分钟搞定 iPhone USB 网络共享:苹果驱动免装 iTunes 的终极避坑指南
Windows 1 分钟搞定 iPhone USB 网络共享:苹果驱动免装 iTunes 的终极避坑指南 深夜的机场候机厅,手机电量只剩 10%,Wi Fi 弱
开发工具Kubernetes 服务网格技术对比:为什么有了 K8s 还需要 Service Mesh
Kubernetes 服务网格技术对比:为什么有了 K8s 还需要 Service Mesh 服务网格(Service Mesh)是 Kubernetes 生态
教程云原生容器编排为什么你的Laravel API需要CORS:跨域请求的终极解决方案
为什么你的Laravel API需要CORS:跨域请求的终极解决方案 在现代Web开发中,跨域资源共享(CORS)已经成为构建API时不可忽视的关键技术。特别是
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考