Cookie missing in HTTP request when using Fetch API

Debugging: a classic mystery game where you are the detective, the victim, and also the murderer.

Suhan ๐ŸŽƒ Wijaya
2 min readNov 2, 2020
Image source: Programmer Humor

Here is an explainer of how cookies work. TLDR:

  • Browser sends HTTP request to server.
  • Server sends HTTP response with Set-Cookie: cookie=monster header, which sets the cookie in the browser.
  • Every subsequent request the browser sends to the server will have the Cookie: cookie=monster header.

I store a CSRF token in a cookie, which is used by the server to validate client-side HTTP POST requests. The server responds with a 403 if the cookie is missing in the HTTP request.

On the client-side, I have been using the cross-fetch package via the ponyfill approach.

import fetch from 'cross-fetch';fetch('/some-route', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: { /* some payload */ }
});

One day, I decided to switch to the polyfill approach in order to use the native window.fetch.

import 'cross-fetch/polyfill';fetch('/some-route', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: { /* some payload */ }
});

A bunch of users started getting 403s.

Image source: Know Your Meme

After scouring Stack Overflow and the interwebs (and many tears later), the answer was in the MDN docs all along:

The users getting 403s were using browsers older than the versions listed above. So this was the fix:

import 'cross-fetch/polyfill';fetch('/some-route', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: { /* some payload */ },
credentials: 'same-origin' // the fix
});

You might ask: what about Internet Explorer, your favorite problem child? Well, since it supports nothing, the polyfill kicks in with a version of fetch that sets same-origin as the default credentials policy, so no 403s.

Today I learned.

๐Ÿ“ซ Hit me up on LinkedIn or email!

--

--