Fetch API, custom request headers, CORS, and cross-origin redirects

Brad picture Brad · Nov 14, 2016 · Viewed 11.4k times · Source

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:

fetch('https://example.com/resource', {
  method: 'GET',
  headers: {
    'X-Brad-Test': 'true'
  },
  cache: 'no-store',
  mode: 'cors'
}).then((res) => {
  const reader = res.body.getReader();
  // etc.
});

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

Access-Control-Allow-Headers: X-Requested-With, Range, If-Range, X-Brad-Test
Access-Control-Allow-Origin: *

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

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

Fetch API cannot load https://example.com/resource. Redirect from 'https://example.com/resource' to 'http://some-other-origin/resource' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.

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.

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.

Two Questions:

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

Answer

Anne picture Anne · Nov 14, 2016

Supporting redirects to requests that require a preflight is very recent change to Fetch (which defines CORS).

https://github.com/whatwg/fetch/commit/0d9a4db8bc02251cc9e391543bb3c1322fb882f2

I believe some implementations have started adjusting their implementations, but this will take some time to reach everyone.