title: Fetch API, custom request headers, CORS, and cross-origin redirects
body: <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>