title: Access-Control-Allow-Origin equals origin but the browser still denies access... why?
body: <p>I have an application (React SPA) that calls a bunch of servers on different subdomains of the application domain, i.e.:</p>
  <ul>
  <li>the web app sits at <code>foo.bar.com</code>,</li>
  <li>and talks to <code>api.foo.bar.com</code> and <code>media.foo.bar.com</code>.</li>
  </ul>
  <p>When accessing <code>api.foo.bar.com</code>, I get an error from the browser (be it Edge, Chrome, or Firefox) telling me that the origin (<code>foo.bar.com</code>) is different from the value of the <code>Access-Control-Allow-Origin</code> response header. However, by inspection of the response, they are the same:</p>
  <p><a href="https://i.stack.imgur.com/UBxUV.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/UBxUV.png" alt="enter image description here" /></a></p>
  <p>(I unfortunately have to obfuscate the address.)</p>
  <p>Those apps are hosted on Kubernetes; the ingress is NGINX, and it's is not providing CORS (cors-enabled annotation is false). Both applications (<code>api</code> and <code>media</code>) are Express apps, and both have the same CORS configuration allowing the specific origin.</p>
  <p>I'm wondering if this has something to do with the redirect - the call to the <code>media...</code> endpoint returns a redirect (302) whose Location is a <code>api...</code> address.</p>
  <p>Other than that, I have no clue what could be wrong. Something is, for sure, because all browsers agree that my request should be blocked (on account of the origin).</p>
  <p>In all cases, I've checked the address multiple times for typos, ending forward-slashes, etc. I've called <code>OPTIONS</code> on those endpoints with cURL and Postman, using all headers or just a few. They always answer the correct address.</p>
  <hr />
  <p>Additional information, as requested:</p>
  <h2>Preflight request:</h2>
  <pre><code>OPTIONS /media/1.0.0/rtsp/hls?feedUrl=https%3A%2F%2Flive.monuv.com.br%2Fa1%2F14298.stream%2Fstr27%2Fchunklist.m3u8%3Fm_hash%3DkhV_hCnKG3nhaNCFaYZxBnoMz-99idQVHiQh80ADW78%253D HTTP/2
  Host: media.aiXXXXXXXXXXXXXX.com
  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
  Accept: */*
  Accept-Language: en-US,en;q=0.5
  Accept-Encoding: gzip, deflate, br
  Access-Control-Request-Method: GET
  Access-Control-Request-Headers: feedurl
  Referer: https://aiXXXXXXXXXXXXXXXX.com/
  Origin: https://aiXXXXXXXXXXXXXXXX.com
  DNT: 1
  Connection: keep-alive
  Sec-Fetch-Dest: empty
  Sec-Fetch-Mode: cors
  Sec-Fetch-Site: same-site
  Pragma: no-cache
  Cache-Control: no-cache
  TE: trailers
  </code></pre>
  <h2>Preflight response:</h2>
  <pre><code>HTTP/2 204 No Content
  date: Fri, 08 Oct 2021 13:33:10 GMT
  x-powered-by: Express
  access-control-allow-origin: https://aiXXXXXXXXXXXXXXXXXX.com
  vary: Origin
  access-control-allow-credentials: true
  access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
  access-control-allow-headers: Content-Type, feedUrl
  strict-transport-security: max-age=15724800; includeSubDomains
  X-Firefox-Spdy: h2
  </code></pre>
  <h2>Request</h2>
  <p>The preflight passes, and the browsers starts a &quot;flight&quot; request:</p>
  <pre><code>GET /media/1.0.0/rtsp/hls?feedUrl=https%3A%2F%2Flive.monuv.com.br%2Fa1%2F14298.stream%2Fstr27%2Fchunklist.m3u8%3Fm_hash%3DkhV_hCnKG3nhaNCFaYZxBnoMz-99idQVHiQh80ADW78%253D HTTP/2
  Host: media.aiXXXXXXXXXXXXXXXXXXXXX.com
  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
  Accept: */*
  Accept-Language: en-US,en;q=0.5
  Accept-Encoding: gzip, deflate, br
  feedUrl: https://live.monuv.com.br/a1/14298.stream/str27/chunklist.m3u8?m_hash=khV_hCnKG3nhaNCFaYZxBnoMz-99idQVHiQh80ADW78%3D
  Origin: https://aiXXXXXXXXXXXXXXXX.com
  DNT: 1
  Connection: keep-alive
  Referer: https://aiXXXXXXXXXXXXXXXXX.com/
  Cookie: ory_kratos_session=MTYzMzYzODY1OHxEdi1CQkFFQ180SUFBUkFCRUFBQVJfLUNBQUVHYzNSeWFXNW5EQThBRFhObGMzTnBiXXXXXXXXXXXXYVc1bkRDSUFJSHBtUWxsaWFsVlJhWGRTVGxSMmIzZHRkbTFqYm5CUlRWVkdkelpPWkRoWnXXXTyqwgK-0Pe0qtZHjNhfU-YoASjg3istMZi672swQ==
  Sec-Fetch-Dest: empty
  Sec-Fetch-Mode: cors
  Sec-Fetch-Site: same-site
  Pragma: no-cache
  Cache-Control: no-cache
  TE: trailers
  </code></pre>
  <h2>Response</h2>
  <pre><code>HTTP/2 302 Found
  date: Fri, 08 Oct 2021 13:33:10 GMT
  content-type: text/plain; charset=utf-8
  content-length: 129
  location: https://api.aiXXXXXXXXXXXXXXXXXX.com/media/1.0.0/hls/streams/19dd149d-f551-4093-b2aa-e5558388d545/hls.m3u8
  x-powered-by: Express
  access-control-allow-origin: https://aiXXXXXXXXXXXXXXXX.com
  vary: Origin, Accept
  access-control-allow-credentials: true
  strict-transport-security: max-age=15724800; includeSubDomains
  X-Firefox-Spdy: h2
  </code></pre>
  <p>At this response, the browser fails saying that the <code>origin</code> don't match the <code>access-control-allow-origin</code>.</p>
  <p><a href="https://i.stack.imgur.com/vFJyM.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vFJyM.png" alt="browser log" /></a></p>
  <p>(<em>the first image was from Edge, since the log was more clear; this log is from Firefox</em>)</p>