- 非簡單請求不可重定向,包括第一個preflight請求和第二個真正的請求都不行。
- 簡單請求可以重定向任意多次,但如需兼容多數瀏覽器,只可進行一次重定向。
- 中間服務器應當同樣配置相關 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 中的錯誤信息為:
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
的解釋見下文。
重定向preflight 請求
任何非 2xx 狀態碼都認為 preflight 失敗, 所以 preflight 不允許重定向。各瀏覽器表現一致不再贅述,可參考 W3C:
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 的怪異行為
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 后的請求不允許重定向。因此:
- 對於簡單請求並且沒有任何 preflight 的情況:瀏覽器會一直跟隨重定向(當然 HTTP 另有規定的除外,如 POST 被 302 時),直到最后一個請求返回或者中間請求的 CORS 驗證失敗(比如
Access-Control-Allow-Origin
設置錯誤)。 - 對於簡單請求但是瀏覽器會發起 preflight 的情況(比如 Safari 對
DNT
的處理):因 preflight 后重定向真正的請求會導致 CORS 失敗,所以多次重定向是不可行的。 - 對於非簡單請求:瀏覽器會直接發起 preflight,后續的重定向都是不允許的因此多次重定向不可行。
總之,如果需要兼容大多數瀏覽器,不論是否為簡單請求都不可以多次重定向。
來源: https://harttle.land/2016/12/30/cors-redirect.html。