就相关规范的实际要求而言,答案分为几个部分:
- 当浏览器必须在内部将源设置为将被序列化为
null的值时
- 当浏览器必须发送 Origin 标头时
以下是详细信息:
当浏览器必须将 origin 设置为将被序列化为 null 的值时
HTML 规范使用术语opaque origin 并将其定义为“内部值”:
没有序列化,它可以从(它被序列化为“null”每个源的 ASCII 序列化)重新创建,唯一有意义的操作是测试相等性
换句话说,HTML 规范在任何地方都说不透明的来源,您可以将其翻译为null。
HTML 规范要求浏览器在这些情况下设置一个不透明的来源或唯一的来源:
- Cross-origin images (including cross-origin
img elements)
- Cross-origin media data (including cross-origin
video and audio elements)
- Any document generated from a
data: URL
- Any
iframe with a sandbox attribute that doesn’t contain the value allow-same-origin
- Any document programmatically created using
createDocument(), etc.
- Any document that does not have a creator browsing context
- Responses that are network errors
- The Should navigation response to navigation request of type from source in target be blocked by Content Security Policy? algorithm returns Blocked when executed on a navigate response
在一种情况下,Fetch 规范要求浏览器将原点设置为“全局唯一标识符”(这基本上与“不透明原点”的含义相同,基本上表示 null...):
- Redirects across origins
URL 规范要求浏览器在以下情况下设置不透明来源:
- For
blob: URLs
- For
file: URLs
-
For any other URLs whose scheme is not one of
http, https, ftp, ws, wss, or gopher。
但是请注意,仅仅因为浏览器在内部设置了一个不透明的来源——本质上是null——并不一定意味着浏览器会发送一个Origin 标头。因此,有关浏览器何时必须发送 Origin 标头的详细信息,请参阅此答案的下一部分。
当浏览器必须发送 Origin 标头时
浏览器发送 Origin 标头用于由 fetch() 或 XHR 调用或 JavaScript 库(axios、jQuery 等)中的 ajax 方法发起的跨域请求 - 但不适用于普通页面导航(也就是说,当您直接在浏览器中打开网页时),而不是(通常)嵌入网页中的资源(例如,不适用于 CSS 样式表、脚本或图像)。
但这种描述是一种简化。当浏览器发送Origin 标头时,除了跨域XHR/fetch/ajax 调用之外,还有一些情况是浏览器发送Origin 标头以获取嵌入式资源。所以下面是更长的答案。
就规范要求而言:规范要求仅针对 Fetch 规范定义为 CORS request 的任何请求发送 Origin 标头:
CORS 请求 是包含 Origin 标头的 HTTP 请求。它不能被可靠地识别为参与 CORS 协议,因为 Origin 标头也包含在方法既不是 GET 也不是 HEAD 的所有请求中。
所以,规范的含义是:Origin 标头在所有跨域请求中发送,但它也始终为所有 POST、PUT、@987654374 发送@ 和 DELETE 请求——即使对于 same-origin POST、PUT、PATCH 和 DELETE 请求(根据 Fetch 中的定义实际上是“CORS 请求”——即使它们是同源的)。*
浏览器必须发送 Origin 标头的其他情况是使用设置了“CORS 标志”的请求的任何情况 — 就 HTTP(S) 请求而言,该标志是 except when the request mode is navigate, websocket, same-origin, or no-cors。
XHR 总是 将模式设置为cors。但是使用 Fetch API,这些请求模式是您可以使用 fetch(…) 方法的 init-object 参数的 mode 字段设置的模式:
fetch("http://example.com", { mode: 'no-cors' }) // no Origin will be sent
字体请求始终将模式设置为 cors,因此始终具有 Origin 标头。
对于任何带有a crossorigin attribute(aka“CORS 设置属性”)的元素,HTML 规范要求浏览器将请求模式设置为cors(并发送Origin 标头)。
否则,对于嵌入式资源——任何具有发起请求的 URL 属性的元素(<script src>、样式表、图像、媒体元素)——请求的模式默认为 no-cors;并且由于这些请求是 GET 请求,这意味着,根据规范,浏览器不会为它们发送 Origin 标头。
当 HTML 表单元素发起 POST 请求时,那些 POSTs 的模式也默认为 no-cors — 就像嵌入式资源的模式默认为 no-cors 一样。但是,与 no-cors 模式 GET 对嵌入式资源的请求不同,浏览器确实会为那些从 HTML 表单元素发起的 no-cors 模式 POSTs 发送 Origin 标头。
原因是,如本答案前面所述,浏览器总是在所有 POST、PUT、PATCH 和 DELETE 请求中发送 Origin 标头。
此外,为了完整起见,为了清楚起见:对于导航,浏览器不发送 Origin 标头。也就是说,如果用户直接导航到某个资源——通过将 URL 粘贴到浏览器地址栏中,或者通过来自另一个 Web 文档的链接——那么浏览器不会发送 Origin 标头。
*要求浏览器为所有CORS请求发送Origin标头的algorithm in the Fetch spec是这样的:
要附加一个请求 Origin 标头,给定一个请求 request,运行以下步骤:
1。让 serializedOrigin 成为使用 request 对请求源进行字节序列化的结果。
2。如果request的response tainting是“cors”或者request的mode是“websocket”,那么
将Origin/serializedOrigin 附加到request 的标头列表中。
3.否则,如果request的方法既不是GET也不是HEAD,
然后:[在这种情况下也发送 Origin 标头]
第 2 步要求在所有跨域请求中发送 Origin 标头——因为所有跨域请求都将其响应污染设置为“cors”。
但是第 3 步还需要为 same-origin POST、PUT、PATCH 和 DELETE 请求发送 Origin 标头(其中根据 Fetch 中的定义,实际上是“CORS 请求”——即使它们是同源的)。
由于change that was made to the spec on 2016-12-09,上面描述了 Fetch 规范当前如何定义要求。在那之前,要求是不同的:
• 以前没有为同源 POST 发送 Origin
• 以前没有从 <form> 发送跨域 POST 的 Origin(没有 CORS)
所以问题描述的 Firefox 行为是规范以前要求的,而不是当前要求的。