As you might know, RFC 6265 indicates that it is allowed to have multiple headers with the Set-Cookie
name.
However, Fetch API doesn't allow to do that because all the methods exposed by its Headers interface (including get()
, set()
, append()
, entries()
and all the rest) have been implemented to merge the values of all the headers with the same name into a single header separated by commas.
For example, if we do this:
var headers = new Headers();
headers.append('content-type', 'text/plain');
headers.append('set-cookie', 'test1=v; Max-Age=0');
headers.append('set-cookie', 'test2=v; Max-Age=0');
headers.append('set-cookie', 'test3=v; Max-Age=0');
and then we try to read the set-cookie
values using get('set-cookie')
, or by iterating the headers
variable using entries()
, we get this:
'set-cookie' : test1=v; Max-Age=0, test2=v; Max-Age=0, test3=v; Max-Age=0
Please notice that the same wrong behaviour also happens if we try to read or manipulate an existing response object having multiple headers with the same name (i.e. created by other frameworks that arguably support such allowed behavior): in other words, it seems like the Fetch API
is completely unable to properly deal with such scenario.
Now, while this behavior is desired for some headers, such as Accept
, the Set-Cookie
header is not parsed correctly by most browsers (including Chrome and Firefox), thus resulting in cookies not being correctly set.
Is that a known bug? If that's the case, is there an usable workaround that can be used to overcome this?
However, Fetch API doesn't allow to do that because all the methods exposed by its Headers interface (including get (), set (), append (), entries () and all the rest) have been implemented to merge the values of all the headers with the same name into a single header separated by commas.
This types cookies were removed when the user shut down the system this types of cookies known as a session cookie. To check this Set-Cookie in action go to Inspect Element -> Network check the response header for Set-Cookie. Supported Browsers: The browsers compatible with HTTP header Set-Cookie are listed below:
When the user agent generates an HTTP request, the user agent MUST NOT attach more than one Cookie header field. Sorry, something went wrong. just surround the next same header by quotation marks and add null char at the beginning.
Origin servers SHOULD NOT fold multiple Set-Cookie header fields into a single header field. The usual mechanism for folding HTTP headers fields (i.e., as defined in [RFC2616]) might change the semantics of the Set-Cookie header field because the %x2C (",") character is used by Set-Cookie in a way that conflicts with such folding.
This is a known "issue" with the standard. It's actually the first note of the Fetch API standard in the Headers
section:
Unlike a
header
list, aHeaders
object cannot represent more than oneSet-Cookie
header. In a way this is problematic as unlike all other headersSet-Cookie
headers cannot be combined, but sinceSet-Cookie
headers are not exposed to client-side JavaScript this is deemed an acceptable compromise. Implementations could chose the more efficient Headers object representation even for a header list, as long as they also support an associated data structure forSet-Cookie
headers.
You can read more or even raise your own issue in the spec's repo.
There are already a few issues discussing the Set-Cookie
case at length though:
You mentioned using workarounds, but this really depends on your use-case.
The note mentions using a secondary structure to handle those.
If you really want to store those cookies in a Headers
object, you could add custom headers to store them:
new Headers([
['X-MyOwn-Set-Cookie-1', 'cookie1=value1'],
['X-MyOwn-Set-Cookie-2', 'cookie2=value2']
]);
Obviously, this is not an acceptable solution for the standard, but maybe your practical considerations might be in line with such a compromise.
As pointed out by this note and @Barmar in the comments, you usually use Set-Cookie
from the server, not the front-end.
For instance, there's no problem setting multiple Set-Cookie
with express
:
test.js
const express = require('express');
const app = express();
const cookies = [
{ key: 'cookie1', value: 'value1' },
{ key: 'cookie2', value: 'value2' },
];
app.get('*', (req, res) => {
console.log(req.url);
for (const { key, value } of cookies) {
res.cookie(key, value, { expires: new Date(Date.now() + 1000 * 60), httpOnly: true });
}
res.status(200).send('Success');
});
app.listen(3000, () => console.log(`Listening on http://localhost:3000/`));
Terminal 1
$ node test.js
Listening on http://localhost:3000/
Terminal 2
$ curl -v http://localhost:3000/
[...]
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Set-Cookie: cookie1=value1; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Set-Cookie: cookie2=value2; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Content-Type: text/html; charset=utf-8
[...]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With