Apache 反向代理和来自上游服务的 304 的 Access-Control-Allow-Origin header 值不正确
Incorrect Access-Control-Allow-Origin header value with Apache reverse proxy and 304 from upstream service
我有一种情况让我有点发疯。我继承了一些基础设施,其中包括 Apache 2.2 反向代理和 DMZ 中的上游服务,该服务通过 REST API.
提供资源
不同子域上有两个应用程序通过反向代理请求相同的 JSON 资源。
当应用程序 #1 (https://one.host.com) 请求资源时,请求 header 如下所示(根据 Chrome DevTools)并且我的资源按预期返回:
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: rest.api.com
Origin: https://one.host.com
Referer: https://one.host.com/...
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
响应 header 看起来像:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Origin: https://one.host.com
Access-Control-Max-Age: 1000
Cache-Control: max-age=0,must-revalidate
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 1906
Content-Type: application/json;charset=UTF-8
Date: Tue, 18 Jan 2022 22:32:35 GMT
ETag: 7a58c125
Keep-Alive: timeout=5, max=100
Server
Vary: Origin
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
如果我随后从应用程序 #2 (https://two.host.com) 请求相同的资源,我会遇到 CORS 错误并且 headers 看起来像这样(根据 Chrome DevTools):
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: rest.api.com
If-None-Match: 7a58c125
Origin: https://two.host.com
Referer: https://two.host.com...
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
和响应 headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Origin: https://one.host.com
Access-Control-Max-Age: 1000
Cache-Control: max-age=0,must-revalidate
Content-Encoding: gzip
Content-Length: 1906
Content-Type: application/json;charset=UTF-8
Date: Tue, 18 Jan 2022 22:38:54 GMT
ETag: 7a58c125
Server
Vary: Origin
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
注意第二组响应header中的Access-Control-Allow-Origin header引用了错误的host/origin,导致CORS错误
我的 Apache 反向代理设置 headers 是这样的:
<IfModule mod_headers.c>
SetEnvIf Referer "^(?:http:\/\/|https:\/\/)[^\/]+" origin_is=[=16=]
Header set Access-Control-Allow-Origin %{origin_is}e
Header set Access-Control-Allow-Credentials true
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, HEAD"
Header set Access-Control-Allow-Headers "X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept"
Header set Access-Control-Max-Age 1000
</IfModule>
当我查看我的 Apache 访问日志时,我会看到第一个请求是 200 或 304,第二个请求总是看到 304。我发现我的反向代理返回 304 很奇怪,但 Chrome DevTools 显示 200 响应(对于 Chrome 显示 200 的相同请求,我在 Fiddler 中看到 304)。
如果我清除缓存并以相反的顺序发出请求,我会遇到同样的问题,但顺序相反。
作为解决方法,我可以从响应 headers 中强制删除 ETag(似乎有效地禁用了缓存),但这是不可取的。
我在想 what/where 可能是什么问题?上游服务不应该为第二个请求返回 304 响应吗? header 不是从 RP 转发到上游服务吗?我的 RP 应该从 304 响应中剥离 ETag 吗?
我明白问题出在哪里了。它归结为使用旧版本的 Apache (v2.4.38) 作为我们的反向代理,它有一个错误导致 CORS headers 从 304 响应中被剥离。升级到 >=2.4.48 应该可以解决问题。
有关此问题的详细信息,请访问:
https://bz.apache.org/bugzilla/show_bug.cgi?id=51223
https://bz.apache.org/bugzilla/show_bug.cgi?id=61820
我有一种情况让我有点发疯。我继承了一些基础设施,其中包括 Apache 2.2 反向代理和 DMZ 中的上游服务,该服务通过 REST API.
提供资源不同子域上有两个应用程序通过反向代理请求相同的 JSON 资源。
当应用程序 #1 (https://one.host.com) 请求资源时,请求 header 如下所示(根据 Chrome DevTools)并且我的资源按预期返回:
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: rest.api.com
Origin: https://one.host.com
Referer: https://one.host.com/...
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
响应 header 看起来像:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Origin: https://one.host.com
Access-Control-Max-Age: 1000
Cache-Control: max-age=0,must-revalidate
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 1906
Content-Type: application/json;charset=UTF-8
Date: Tue, 18 Jan 2022 22:32:35 GMT
ETag: 7a58c125
Keep-Alive: timeout=5, max=100
Server
Vary: Origin
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
如果我随后从应用程序 #2 (https://two.host.com) 请求相同的资源,我会遇到 CORS 错误并且 headers 看起来像这样(根据 Chrome DevTools):
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: rest.api.com
If-None-Match: 7a58c125
Origin: https://two.host.com
Referer: https://two.host.com...
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
和响应 headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Origin: https://one.host.com
Access-Control-Max-Age: 1000
Cache-Control: max-age=0,must-revalidate
Content-Encoding: gzip
Content-Length: 1906
Content-Type: application/json;charset=UTF-8
Date: Tue, 18 Jan 2022 22:38:54 GMT
ETag: 7a58c125
Server
Vary: Origin
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
注意第二组响应header中的Access-Control-Allow-Origin header引用了错误的host/origin,导致CORS错误
我的 Apache 反向代理设置 headers 是这样的:
<IfModule mod_headers.c>
SetEnvIf Referer "^(?:http:\/\/|https:\/\/)[^\/]+" origin_is=[=16=]
Header set Access-Control-Allow-Origin %{origin_is}e
Header set Access-Control-Allow-Credentials true
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, HEAD"
Header set Access-Control-Allow-Headers "X-Requested-With, Referer, Origin, Content-Type, SOAPAction, Authorization, Accept"
Header set Access-Control-Max-Age 1000
</IfModule>
当我查看我的 Apache 访问日志时,我会看到第一个请求是 200 或 304,第二个请求总是看到 304。我发现我的反向代理返回 304 很奇怪,但 Chrome DevTools 显示 200 响应(对于 Chrome 显示 200 的相同请求,我在 Fiddler 中看到 304)。 如果我清除缓存并以相反的顺序发出请求,我会遇到同样的问题,但顺序相反。 作为解决方法,我可以从响应 headers 中强制删除 ETag(似乎有效地禁用了缓存),但这是不可取的。
我在想 what/where 可能是什么问题?上游服务不应该为第二个请求返回 304 响应吗? header 不是从 RP 转发到上游服务吗?我的 RP 应该从 304 响应中剥离 ETag 吗?
我明白问题出在哪里了。它归结为使用旧版本的 Apache (v2.4.38) 作为我们的反向代理,它有一个错误导致 CORS headers 从 304 响应中被剥离。升级到 >=2.4.48 应该可以解决问题。
有关此问题的详细信息,请访问: https://bz.apache.org/bugzilla/show_bug.cgi?id=51223 https://bz.apache.org/bugzilla/show_bug.cgi?id=61820