I have a situation that's driving me a little bit crazy. I've inherited some infrastructure that includes an Apache 2.2 reverse proxy and an upstream service in the DMZ that serves up resources through a REST API.
There are two applications on different subdomains that request the same JSON resource via the reverse proxy.
When application #1 (https://one.host.com) requests the resource the request headers look like the following (as per Chrome DevTools) and my resource is returned as expected:
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
The response headers look like:
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
If I then request the same resource from application #2 (https://two.host.com) I encounter a CORS error and the headers look like this (as per 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
and response 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
Notice that the Access-Control-Allow-Origin header in the second set of response headers refers to the wrong host/origin, which leads to the CORS error.
My Apache reverse proxy sets headers like this:
<IfModule mod_headers.c>
SetEnvIf Referer "^(?:http:\/\/|https:\/\/)[^\/]+" origin_is=$0
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>
When I look at my Apache access logs, I will see either a 200 or 304 for the first request, and always see a 304 for the second request. I find it odd that my reverse proxy is returning a 304, yet Chrome DevTools shows a 200 response (I see a 304 in Fiddler for the same request that Chrome shows a 200 for). If I clear my cache and make the requests in the reverse order, I encounter the same issue, but in reverse. I can brute force remove the ETag from the response headers (effectively disabling caching it seems) as a workaround, but this isn't desireable.
I'm wondering what/where the problem may be? Should the upstream service not be responding with a 304 for the second request? Is a header not being forwarded from the RP to the upstream service? Should my RP be stripping the ETag from 304 response?
I figured out what the issue is. It comes down to using an older version of Apache (v2.4.38) as our reverse proxy which has a bug that results in CORS headers being stripped from 304 responses. Upgrading to >=2.4.48 should resolve the issue.
Details about the issue can be found at: https://bz.apache.org/bugzilla/show_bug.cgi?id=51223 https://bz.apache.org/bugzilla/show_bug.cgi?id=61820