CORS 跨域中的 preflight 请求
我们知道借助 Access-Control-Allow-Origin
响应头字段可以允许跨域 AJAX,
对于非 简单请求,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求),
该请求成功后才会发送真正的请求。
这一设计旨在确保服务器对 CORS 标准知情,以保护不支持 CORS 的旧服务器。
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。如有疏漏、谬误、侵权请通过评论或 邮件 指出。