中间服务器应当一样配置相关 CORS 响应头。跨域
当跨域请求被重定向时,中间服务器返回的 CORS 相关的响应头应当与最终服务器保持一致。 任何一级的 CORS 失败都会致使 CORS 失败。这些头字段包括Access-Control-Allow-Origin
, Access-Control-Allow-Credentials
等。浏览器
响应 preflight
的头字段包括Access-Control-Allow-Headers
, Access-Control-Allow-Methods
等。 由于 preflight 不容许重定向(见下文),因此中间服务器也就没必要管这些 preflight 头字段。服务器
若是中间服务器未设置Access-Control-Allow-Origin
,在 Chrome 中的错误信息为:cors
XMLHttpRequest cannot load http://mid.com:4001/redirect.
Redirect from 'http://mid.com:4001/redirect' to 'http://index.com/access-control-allow-origin-wildcard'
has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://index.com:4001' is therefore not allowed access.tcp
若是最终服务器未设置Access-Control-Allow-Origin
,在 Chrome 中的错误信息为:测试
XMLHttpRequest cannot load http://index.com:4001/access-control-allow-origin-not-set.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'null' is therefore not allowed access.ui
Origin 变成 null 的解释见下文。调试
任何非 2xx 状态码都认为 preflight 失败
, 因此 preflight 不容许重定向。各浏览器表现一致再也不赘述,可参考 W3C:code
The following request rules are to be observed while making the preflight request:
If the end user cancels the request Apply the abort steps.
If the response has an HTTP status code that is not in the 2xx range Apply the network error steps.
– W3C CORS Recommandation: Cross-Origin Request with Preflight开发
对于简单请求浏览器会跳过 preflight 直接发送真正的请求。 该请求被重定向后浏览器会直接访问被重定向后的地址,也能够跟随屡次重定向。 但重定向后请求头字段origin会被设为"null"(被认为是 privacy-sensitive context
)。 这意味着响应头中的Access-Control-Allow-Origin
须要是*
或null
(该字段不容许多个值)。
即便浏览器给简单请求设置了非简单头字段(如DNT)时,也应当继续跟随重定向且不校验响应头的DNT (由于它属于User Agent Header,
浏览器应当对此知情)。 参考 W3C 对简单请求的处理要求:
If the manual redirect flag is unset and the response has an HTTP status code of 301, 302, 303, 307, or 308 Apply the redirect steps. – W3C CORS Recommendation
OSX 下 Chrome 的行为是标准的,即便设置了DNT也会直接跟随重定向。
Safari 在设置DNT字段后,会向重定向后的地址首先发起 preflight(多是它忘记了该头部是本身设置的?)。 这一行为在桌面 Safari 的隐身模式,以及 iOS 不少浏览器中均可以观察到。 Safari 远程调试 iPhone,遇此行为调试器会崩掉(笔者试了3个 Mac+iPhone Pair)。
建议使用tcpdump或者写一个简单的 CORS 服务器来调试。 在 OPTIONS 请求中,会带有Access-Control-Request-Headers
来声明须要发送dnt。
Access-Control-Request-Headers: 'dnt, accept-language, origin'
这意味着为了 Safari 系列的浏览器(包括 iOS 平台的多数浏览器), 重定向简单 CORS 请求仍然须要实现 OPTIONS 方法(虽然咱们发的只是是简单请求)。 而且在Access-Control-Allow-Headers
响应头字段添加dnt
声明。 不然 Safari 会认为 CORS 失败:
XMLHttpRequest cannot load http://index.com:4001/access-control-allow-origin-wildcard.
Request header field DNT is not allowed by Access-Control-Allow-Headers.
为了轻松地让 CORS preflight 成功,测试环境中能够简单地将请求头Access-Control-Request-Headers
的内容直接设置到响应头的Access-Control-Allow-Headers
。
非简单请求是 preflight 成功后才发送实际的请求。 preflight 后的实际请求不容许重定向,不然会致使 CORS 跨域失败。
虽然在 Chrome 开发版中会对重定向后的地址再次发起 preflight,但该行为并不标准。 W3C Recommendation中提到真正的请求返回301, 302, 303, 307, 308都会断定为错误:
This is the actual request. Apply the make a request steps and observe the request rules below while making the request. If the response has an HTTP status code of 301, 302, 303, 307, or 308 Apply the cache and network error steps. – W3C CORS Recommendation
在 Chrome 中错误信息是Request requires preflight, which is disallowed to follow cross-origin redirect:
XMLHttpRequest cannot load http://mid.com:4001/cross-origin-redirect-with-preflight.
Redirect from 'http://mid.com:4001/cross-origin-redirect-with-preflight' to 'http://dest.com:4001/access-control-allow-origin-wildcard'
has been blocked by CORS policy: Request requires preflight,
which is disallowed to follow cross-origin redirect.
在 Safari 中的错误信息是Cross-origin redirection denied by Cross-Origin Resource Sharing policy.:
XMLHttpRequest cannot load http://mid.com:4001/redirect.
Cross-origin redirection denied by Cross-Origin Resource Sharing policy.
屡次重定向涉及的一个关键问题是:preflight 后的请求不容许重定向。所以:
总之,若是须要兼容大多数浏览器,不管是否为简单请求都不能够屡次重定向。