Today (actually yesterday) I learned that when sending FormData
with fetch
, you should never set the Content-Type: multipart/form-data
by yourself—you have to let the browser do it for you.
For example if we do this:
const body = new FormData();
body.append('foo', 'bar');
const method = 'POST';
const headers = {
'Content-Type': 'multipart/form-data'
};
fetch('https://example.com/', {method, body, headers});
We’ll end up making this request (I stripped irrelevant headers):
> POST / HTTP/1.1
> Host: https://example.com
> Content-Type: multipart/form-data
> Content-Length: 175
-----------------------------109074902510780475434212898387
Content-Disposition: form-data; name="foo"
bar
-----------------------------109074902510780475434212898387--
Which won’t work. See the -----------------------------109074902510780475434212898387
separator that was added by the browser in our multipart body? It needs to be in the Content-Type
as well.
But when we omit the Content-Type
header in our request (and let the browser handle it for us):
const body = new FormData();
body.append('foo', 'bar');
const method = 'POST';
const headers = {};
fetch('https://example.com/', {method, body, headers});
We’ll end up making this request:
> POST / HTTP/1.1
> Host: https://example.com
> Content-Type: multipart/form-data; boundary=---------------------------109074902510780475434212898387
> Content-Length: 175
-----------------------------109074902510780475434212898387
Content-Disposition: form-data; name="foo"
bar
-----------------------------109074902510780475434212898387--
Which will now work because the ---------------------------109074902510780475434212898387
boundary set by the browser is present in both the body and the Content-Type
header.
It turns out, boundaries are indeed important! 😅