Cross-domain cookies are not allowed (i.e. site A cannot set a cookie on site B).
But once a cookie is set by site A, you can send that cookie even in requests from site B to site A (i.e. cross-domain requests):
XMLHttpRequest
from a different domain cannot set cookie values for their own domain unlesswithCredentials
is set to true before making the request. The third-party cookies obtained by settingwithCredentials
to true will still honor same-origin policy and hence can not be accessed by the requesting script throughdocument.cookie
or from response headers.
Make sure to do these things:
- When setting the cookie in a response
- The
Set-Cookie
response header includesSameSite=None
if the requests are cross-site (note a request fromwww.web.dev
tostatic.web.dev
is actually a same-site request, and can useSameSite=Strict
) - The
Set-Cookie
response header should include theSecure
attribute if served over HTTPS; as seen here and here
- The
- When sending/receiving the cookie:
- The request is made with
withCredentials: true
, as mentioned in other answers here and here, including the original request whose response sets the cookie set in the first place- For the fetch API, this attribute is
credentials: 'include'
, vswithCredentials: true
- For jQuery’s ajax method, note you may need to supply argument
crossDomain: true
- For the fetch API, this attribute is
- The server response includes cross-origin headers like
Access-Control-Allow-Origin
,Access-Control-Allow-Credentials
,Access-Control-Allow-Headers
, andAccess-Control-Allow-Methods
- As @nabrown points out: “Note that the “
Access-Control-Allow-Origin
” cannot be the wildcard (*
) value if you use thewithCredentials: true
” (see @nabrown’s comment which explains one workaround for this.
- As @nabrown points out: “Note that the “
- The request is made with
- In general:
- Your browser hasn’t disabled 3rd-party cookies. (* see below)
Things that you don’t need (just use the above):
domain
attribute in the Set-Cookie; you can choose a root domain (i.e.a.example.com
can set a cookie with a domain value ofexample.com
, but it’s not necessary; the cookie will still be sent toa.example.com
, even if sent fromb.other-site.com
- For the cookie to be visible in Chrome Dev Tools, “Application” tab; if the value of cookie
HttpOnly
attribute is true, Chrome won’t show you the cookie value in the Application tab (it should show the cookie value when set in the initial request, and sent in subsequent responses wherewithCredentials: true
)
Let’s clarify a “domain” vs a “site”; a quick reminder of “anatomy of a URL” helps me. In this URL https://example.com:8888/examples/index.html
, remember these main parts (got from this paper):
- the “protocol”:
https://
- the “hostname/host”:
example.com
- the “port”:
8888
- the “path”:
/examples/index.html
.
Notice the difference between “path” and “site” for Cookie purposes. “path” is not security-related; “site” is security-related:
path
Servers can set a Path
attribute in the Set-Cookie
, but it doesn’t seem security related:
Note that
path
was intended for performance, not security. Web pages having the same origin still can access cookie viadocument.cookie
even though the paths are mismatched.
site
The SameSite attribute, according to web.dev article, can restrict or allow cross-site cookies; but what is a “site”?
It’s helpful to understand exactly what ‘site’ means here. The site is the combination of the domain suffix and the part of the domain just before it. For example, the
www.web.dev
domain is part of theweb.dev
site…
This means a request to static.web.dev
from www.web.dev
, is a sameSite request.
The public suffix list defines this, so
it’s not just top-level domains like .com but also includes services
likegithub.io
This means a request to your-project.github.io
from my-project.github.io
, is a a cross-site request.
This means what’s to the left of the public suffix; is the subdomain (but the subdomain is a part of the host; see the BONUS reply in this answer)
www
is the subdomain inwww.web.dev
; same site asstatic.web.dev
your-project
is the domain inyour-project.github.io
; separate site asmy-project.github.io
In this URL https://www.example.com:8888/examples/index.html
, remember these parts:
- the “protocol”:
https://
- the “hostname” aka “host”:
example.com
- (in cases like “en.wikipedia.org”, the entire “en.example.com” is also a hostname)
- the “port”:
8888
- the “site”:
example.com
- the “domain”:
example.com
- the “subdomain”:
www
- the “path”:
/examples/index.html
Useful links:
- https://web.dev/samesite-cookies-explained/
- https://jisajournal.springeropen.com/articles/10.1186/1869-0238-4-13
- https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03
- https://inst.eecs.berkeley.edu/~cs261/fa17/scribe/web-security-1.pdf
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
(Be careful; I was testing my feature in Chrome Incognito tab; according to my chrome://settings/cookies
; my settings were “Block third party cookies in Incognito”, so I can’t test Cross-site cookies in Incognito.)