當跨域請求被重定向時,中間服務器返回的 CORS 相關的響應頭應當與最終服務器保持一致。 任何一級的 CORS 失敗都會致使 CORS 失敗。這些頭字段包括Access-Control-Allow-Origin
, Access-Control-Allow-Credentials
等。html
響應 preflight 的頭字段包括
Access-Control-Allow-Headers
,Access-Control-Allow-Methods
等。 由於 preflight 不容許重定向(見下文),因此中間服務器也就沒必要管這些 preflight 頭字段。git
若是中間服務器未設置Access-Control-Allow-Origin
,在 Chrome 中的錯誤信息爲:github
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.
若是最終服務器未設置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.
Origin 變成
null
的解釋見下文。瀏覽器
任何非 2xx 狀態碼都認爲 preflight 失敗, 因此 preflight 不容許重定向。各瀏覽器表現一致再也不贅述,可參考 W3C:服務器
The following request rules are to be observed while making the preflight request:cookie
If the end user cancels the request Apply the abort steps.cors
If the response has an HTTP status code that is not in the 2xx range Apply the network error steps.tcp
– 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 後的請求不容許重定向。所以:
Access-Control-Allow-Origin
設置錯誤)。DNT
的處理):因 preflight 後重定向真正的請求會致使 CORS 失敗,因此屢次重定向是不可行的。總之,若是須要兼容大多數瀏覽器,不管是否爲簡單請求都不能夠屢次重定向。