我们知道借助 Access-Control-Allow-Origin 响应头字段可以允许跨域 AJAX, 对于非 简单请求CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。 这一设计旨在确保服务器对 CORS 标准知情,以保护不支持 CORS 的旧服务器。

Flowchart_showing_Simple_and_Preflight_XHR

Wikipedia: https://upload.wikimedia.org/wikipedia/commons/c/ca/Flowchart_showing_Simple_and_Preflight_XHR.svg

简单请求

简单请求 具体是指请求方法是 简单方法 且请求头是 简单头 的 HTTP 请求。具体地,

  • 简单方法 包括 GET, HEAD, POST
  • 简单头 包括:Accept, Accept-Language, Content-Language,以及值为 application/x-www-form-urlencoded, multipart/form-data, text/plain 其中之一的 Content-Type 头。

对于非简单请求浏览器会首先发送 OPTIONS 请求(成为 preflight), 例如添加一个自定义头部 x-foo 的 HTTP 请求:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.setRequestHeader('x-foo', 'bar');
xhr.send();

服务器需要成功响应(2xx)并在 Access-Control-Alow-Headers 中包含 x-foo (因为它不是 简单头部):

OPTIONS /origin-redirect-with-preflight 200
Access-Control-Allow-Headers:x-foo
Access-Control-Allow-Origin:http://index.com:4001
Connection:keep-alive
Content-Length:0

Access-Control-Request-Headers

Access-Control-Request-Headers 是 preflight 请求中用来标识真正请求将会包含哪些头部字段, preflight 请求本身不会发送这些头字段。 例如上述请求中 Access-Control-Request-Headers 字段的值应该是 x-foo。 服务器应当在对应的 Access-Control-Allow-Headers 响应头中包含这些字段。 否则即使返回 200 preflight 也会失败:

XMLHttpRequest cannot load http://mid.com:4001/access-control-allow-origin-wildcard.
Request header field x-foo is not allowed by Access-Control-Allow-Headers in preflight response.

关于 DNT 请求头

有些浏览器(如 Safari 隐身模式)会在请求中添加 DNT 头, 但浏览器不会(也不应)因此而发起 preflight。 因为这一请求头是浏览器添加的,也应当对此知情。 所以响应头中也不需要包含 Access-Control-Allow-Headers, 参照 W3C Recommendation,满足以下条件即可跳过 preflight:

For request method there either is a method cache match or it is a simple method and the force preflight flag is unset. For every header of author request headers there either is a header cache match for the field name or it is a simple header.

只要所有 "Author Header" 都是简单头即可跳过 preflight, 这里虽然 "DNT" 头不属于简单头,但它也不属于 "Author Header",它是 "User-Agent Header"。 因此它不会导致触发 preflight。但是这一简单请求如果被重定向情况会变得相当复杂, 请参考 重定向 CORS 跨域请求 一文中的讨论。

本文采用 知识共享署名 4.0 国际许可协议(CC-BY 4.0)进行许可,转载注明来源即可: https://harttle.land/2016/12/30/cors-preflight.html。如有疏漏、谬误、侵权请通过评论或 邮件 指出。