Access-Control-Allow-Origin 带有 ETag header 的响应似乎被缓存了,尽管响应也有 Vary: Origin

Access-Control-Allow-Origin response with ETag header seems to be getting cached despite response also having Vary: Origin

我有一个 rails API 服务 mywebsite.com 和 app.mywebsite.com rack-cors 配置为允许我从两者发出请求。 API 位于 api.mywebsite.com.

如果我从 mywebsite.com 调用端点,一切都会按预期进行。但是,如果我随后从 app.myswebsite.com 进行相同的调用,我会收到错误消息:

Access to fetch at 'https://api.mywebsite.com/api/v1/endpoint' from origin 'https://app.mywebsite.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://mywebsite.com' that is not equal to the supplied origin.

我在 rack-cors 中设置了调试,可以看到正确的 Access-Control-Allow-Origin 正在发送正确的 header,它似乎没有发送到浏览器。

我发现如果我清除了我的缓存,那么我就能够成功地从应用程序进行调用。mywebsite.com 但随后收到来自 mywebsite.com 的错误:

Access to fetch at 'https://api.mywebsite.com/api/v1/endpoint' from origin 'https://mywebsite.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://app.mywebsite.com' that is not equal to the supplied origin.

简而言之,我的浏览器似乎正在缓存它收到的第一个 'Access-Control-Allow-Origin' header。

我读过我需要设置 Vary 响应 header 但我已经将其设置为 Origin。

编辑: 从工作请求 (mywebsite.com)

请求 headers
Accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Type: application/json
Cookie: _my_website_session=abc123
Host: api.mywebsite.com
Origin: https://mywebsite.com
Referer: https://mywebsite.com/
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36

响应 header 工作请求(mywebsite.com)

Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE
Access-Control-Allow-Origin: https://mywebsite.com
Access-Control-Expose-Headers
Access-Control-Max-Age: 7200
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Type: application/json; charset=utf-8
Date: Fri, 22 Oct 2021 09:19:31 GMT
ETag: W/"ceb3066459b786782d836ac9e51cd349"
Keep-Alive: timeout=5, max=99
Referrer-Policy: strict-origin-when-cross-origin
Server: Apache
Set-Cookie: _my_website_session=abc123; path=/; HttpOnly
Transfer-Encoding: chunked
Vary: Origin
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 8fefc93c-b320-4276-acd4-8177b7745068
X-Runtime: 0.021539
X-XSS-Protection: 1; mode=block

请求 header 来自错误请求 (app.mywebsite.com):

Accept: application/json
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Type: application/json
Cookie: _my_website_session=abc_123
Host: api.mywebsite.com
If-None-Match: W/"ceb3066459b786782d836ac9e51cd349"
Origin: https://app.mywebsite.com
Referer: https://app.mywebsite.com/
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36

来自错误请求的响应 Headers (app.mywebsite.com)

Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE
Access-Control-Allow-Origin: https://mywebsite.com
Access-Control-Expose-Headers
Access-Control-Max-Age: 7200
Cache-Control: max-age=0, private, must-revalidate
Content-Type: application/json; charset=utf-8
Date: Fri, 22 Oct 2021 09:19:32 GMT
ETag: W/"ceb3066459b786782d836ac9e51cd349"
Referrer-Policy: strict-origin-when-cross-origin
Server: Apache
Set-Cookie: _my_website_session=abc123; path=/; HttpOnly
Vary: Origin
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 8fefc93c-b320-4276-acd4-8177b7745068
X-Runtime: 0.021539
X-XSS-Protection: 1; mode=block

编辑 2 Mac 上的 Chrome 上存在此问题。在 Safari 上一切正常 我还可以看到请求正确地击中了控制器,问题似乎只是用户代理中的响应。

我终于弄明白了。

Chrome 仍将使用缓存的 header,即使来源不同并且 Vary header 存在并设置为 'Origin'(如果 ETag 仍然匹配)。

要解决此问题,请取消设置 ETag header 或根据请求来源更改它。