fetch() cannot set cookies received from the server?
I have found the solution. The core of this problem being that my button to trigger the fetch
is on http://localhost:3000/
. The server is on http://localhost:5555/
(I am simulating real environment on my own machine)
The problem is that this fetch
call
async trySetCookie() { await fetch("http://localhost:5555/s",{ method: 'GET', credentials: 'same-origin' }) }
Without credentials
, the browser cannot send or receive cookies via fetch
(https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
With credentials
as same-origin
I can see the cookies coming from the server in the Set-Cookie
response header, but nothing is being stored in the browser. One strange thing is that this response always have HttpOnly
tagged after the cookie string regardless of my {httpOnly : true/false}
settings on the server. In the case of manually using the browser to the page to do GET request, HttpOnly
is being respected as usual, and the cookies are set.
So the solution is to set credentials
as include
to allow cross-origin cookie sending.
async trySetCookie() { await fetch("http://localhost:5555/s",{ method: 'GET', credentials: 'include' }) }
Also, on the server side you need to allow a particular origin manually with new headers:
app.get("/s", (req,res) => { res.cookie("bsaSession", req.session.id, {httpOnly:false}) res.header('Access-Control-Allow-Origin', 'http://localhost:3000') res.header('Access-Control-Allow-Credentials','true' res.send("set")})
Not doing this results in
XMLHttpRequest cannot load http://localhost:5555/s. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
But the cookie will be set regardless of this error. Still nice to include that header to silence the error.
If you are using cors
middleware for Express
it is even easier. You can just use these options
var corsOptions = { origin: 'http://localhost:3000', credentials: true}app.use(cors(corsOptions))
And of course credentials: 'include'
is still required at the client side.
5argon's solution was great for me otherwise, but I had to set origin
in express cors to true
. So in backend:
app.use( cors({ origin: true, credentials: true, }));
And in fetch:
fetch("http://localhost:5555/s", { method: 'GET', credentials: 'include'})