Fetch API, custom request headers, CORS, and cross-origin redirects
<p>I need to make an HTTP GET request with custom request headers in-browser and process the result as it streams in.  The Fetch API is ideal for this:</p>

<pre class="lang-js prettyprint-override"><code>fetch('https://example.com/resource', {
  method: 'GET',
  headers: {
    'X-Brad-Test': 'true'
  },
  cache: 'no-store',
  mode: 'cors'
}).then((res) =&gt; {
  const reader = res.body.getReader();
  // etc.
});
</code></pre>

<p>This works quite well.  Since there are custom headers, the browser pre-flights the request with an OPTIONS request to <code>/resource</code>.  I have configured my server to respond with a <code>204 No Content</code> and the following headers:</p>

<pre><code>Access-Control-Allow-Headers: X-Requested-With, Range, If-Range, X-Brad-Test
Access-Control-Allow-Origin: *
</code></pre>

<p>The browser is happy with this, then makes a GET request, the server returns a <code>200 OK</code> with the data, and the browser allows me to access the response headers and body.</p>

<p>The problem comes in when there is a redirect.  The <code>OPTIONS</code> request succeeds with the <code>204 No Content</code> and the same headers as before.  The browser makes the correct <code>GET</code> request, and on the server I send a <code>302</code> with a <code>Location:</code> header.  Chrome throws the following error:</p>

<blockquote>
  <p>Fetch API cannot load <a href="https://example.com/resource" rel="noreferrer">https://example.com/resource</a>. Redirect from '<a href="https://example.com/resource" rel="noreferrer">https://example.com/resource</a>' to '<a href="http://some-other-origin/resource" rel="noreferrer">http://some-other-origin/resource</a>' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.</p>
</blockquote>

<p>This was unexpected, and seems nonsensical to me.  I expected the browser to follow the redirect, and do another pre-flight request for this new location, but it didn't do that.</p>

<p>Stranger still is that I can sort of hack around this client-side.  I can make an HTTP request without my custom header, figure out where I ended up after redirects by looking at the Response object, then make a second request at the new target with my custom headers.  This doesn't work in all cases of course, and I'd rather not rely on this hack.  I'd rather find a proper way.</p>

<p>Two Questions:</p>

<ul>
<li>What is the proper way to allow the client to follow redirects?  Is there some sort of <code>Access-Control-*</code> header I can use?</li>
<li>Why does this restriction exist?  What security issue is prevented by not following and running pre-flight on the followed URL?</li>
</ul>