How to share cookies cross origin? More specifically, how to use the Set-Cookie
header in combination with the header Access-Control-Allow-Origin
?
Here's an explanation of my situation:
I am attempting to set a cookie for an API that is running on localhost:4000
in a web app that is hosted on localhost:3000
.
It seems I'm receiving the right response headers in the browser, but unfortunately they have no effect. These are the response headers:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:3000 Vary: Origin, Accept-Encoding Set-Cookie: token=0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Max-Age=86400; Domain=localhost:4000; Path=/; Expires=Tue, 19 Sep 2017 21:11:36 GMT; HttpOnly Content-Type: application/json; charset=utf-8 Content-Length: 180 ETag: W/"b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ" Date: Mon, 18 Sep 2017 21:11:36 GMT Connection: keep-alive
Furthermore, I can see the cookie under Response Cookies
when I inspect the traffic using the Network tab of Chrome's developer tools. Yet, I can't see a cookie being set in in the Application tab under Storage/Cookies
. I don't see any CORS errors, so I assume I'm missing something else.
Any suggestions?
I'm using the request module in a React-Redux app to issue a request to a /signin
endpoint on the server. For the server I use express.
Express server:
res.cookie('token', 'xxx-xxx-xxx', { maxAge: 86400000, httpOnly: true, domain: 'localhost:3000' })
Request in browser:
request.post({ uri: '/signin', json: { userName: 'userOne', password: '123456'}}, (err, response, body) => { // doing stuff })
I am setting request and response headers now like crazy now, making sure that they are present in both the request and the response. Below is a screenshot. Notice the headers Access-Control-Allow-Credentials
, Access-Control-Allow-Headers
, Access-Control-Allow-Methods
and Access-Control-Allow-Origin
. Looking at the issue I found at Axios's github, I'm under the impression that all required headers are now set. Yet, there's still no luck...
To allow receiving & sending cookies by a CORS request successfully, do the following.
Back-end (server) HTTP header settings:
Set the HTTP header Access-Control-Allow-Credentials
value to true
.
Make sure the HTTP headers Access-Control-Allow-Origin
and Access-Control-Allow-Headers
are set. Don't use a wildcard *
. When you set the allowed origin make sure to use the entire origin including the scheme, i.e. http is not same as https in CORS.
For more info on setting CORS in express js read the docs here.
Cookie settings: Cookie settings per Chrome and Firefox update in 2021:
SameSite=None
Secure
When doing SameSite=None
, setting Secure
is a requirement. See docs on SameSite and on requirement of Secure. Also note that Chrome devtools now have improved filtering and highlighting of problems with cookies in the Network tab and Application tab.
Front-end (client): Set the XMLHttpRequest.withCredentials
flag to true
, this can be achieved in different ways depending on the request-response library used:
ES6 fetch() This is the preferred method for HTTP. Use credentials: 'include'
.
jQuery 1.5.1 Mentioned for legacy purposes. Use xhrFields: { withCredentials: true }
.
axios As an example of a popular NPM library. Use withCredentials: true
.
Avoid having to do cross site (CORS) stuff altogether. You can achieve this with a proxy. Simply send all traffic to the same top level domain name and route using DNS (subdomain) and/or load balancing. With Nginx this is relatively little effort.
This approach is a perfect marriage with JAMStack. JAMStack dictates API and Webapp code to be completely decoupled by design. More and more users block 3rd party cookies. If API and Webapp can easily be served on the same host, the 3rd party problem (cross site / CORS) dissolves. Read about JAMStack here or here.
It turned out that Chrome won't set the cookie if the domain contains a port. Setting it for localhost
(without port) is not a problem. Many thanks to Erwin for this tip!