An online markdown blog and knowledge repository.
CORS is a safety feature that restricts certain types of traffic between web apps and servers.
In past projects I have run into CORS-related issues and it has taken lots of time to resolve these.
This reference documentation is meant to be a high-level review of how and why CORS works, with links to references to more detail.
CORS: Cross Origin Resource Sharing on MDN
These do not trigger CORS Preflight checks.
Simple Requests meet the following conditions:
Note: Webkit and Safari have som additional limitations.
An XHR might cause browser to send a simple request that will include 'Origin: https://mysite.foo'.
Server might respond 'HTTP/1.1 200 OK' and include 'Access-Control-Allow-Origin: *' which means resource can be accessed by any origin.
If the server wanted to restrict to specific origins it would instead send: 'Access-Control-Allow-Origin: https://mysite.foo'.
Note: Credentialed requests must include a non-wildcard 'Access-Control-Allow-Origin' header response.
See full explanation of access control scenarios at MDN.
Browser sends an HTTP Req using 'OPTIONS'. This asks the server if request is safe to send.
A request might include a custom XHR Header and a 'Content-Type: text/xml' => Both of these make this a Preflighted Request and not a Simple Request.
The server replies with CORS origin URL, allowed methods, the allowed Headers (including allowed custom headers), and an age (expiration).
The client can then be sent to the server, and the Server will send the appropriate response.
Note: 'Access-Control-Request-*' headers are only needed for the OPTIONS request.
There is lingering behavior in browsers that completely disallows redirects in a Preflight Request type.
To work around this:
If those options are not possible:
Note: Authorization Headers in the request will always trigger Preflight Requests. This means that server-side changes must be made to work around the issue.
Credentials are not sent by default in XHR (request) or Fetch invokations.
XRH must include 'WithCredentials' flag (see code example) to allow cookie-setting by separate URL.
This gets a little complicated. Check out the example on MDN that details what is going on and why.
Wildcard not allowed in 'Access-Control-Allow-Origin' response header.
Response header must have explicit allow origin list.
Wildcard not allowed in 'Access-Control-Allow-Headers' response header.
Response header must have explicit allow headers list.
Wildcard not allowed in 'Acces-Control-Allow-Methods' response header.
Response header must have explicit allow methods list.
Subject to cookie policies.
See Resources for the blog post where I took these key takeaways:
Content-type
: Tells the server what content to except from the caller.X-Content-Type-Options: nosniff
: Header tells server to not parse content unless content-type is set. This covers lots of 'other origin' requests too, and led to protection called CORB.SameSite
cookie attribute: The 2nd site opts-in to cookies sent with the request (e.g. login state).same-origin
policy.XMLHttpRequest
grew out of new ActiveXObject('Microsoft.XMLHTTP')
and requires same-origin.postMessage
-- this worked for frame-to-frame traffic.Access-Control-Allow-Origin: *
(used today).Sec-Fetch-Mode
header, which shows 'cors' or 'no-cors' (Chrome, Firefox).crossorigin
attribute e.g. <link crossorigin rel="" href="" />
origin
for all requests (e.g. WebSocket, POST, GET, etc) as it triggers a 'cors' flag so does not work.Set-Cookie
). Access-Control-Expose-Headers:
can be used with wildcard or certain header types.Access-Control-Allow-Origin
so the client gets a new header, rather than using the cached one.Vary: Cookie
so that 'only cached version of this state of Cookie header match to original request can be served'. Vary: Origin
can be used as a flag to indicate a cors request (but is not a guarantee). Vary: Origin, Cookie
allows checking for both which can help work around this issue.Access-Control-Allow-Origin: *
.Access-Control-Allow-Origin:*
and Vary: Cookie
. This fixes browser and CDN Cache handling of cors requests with private data.Access-Control-Allow-Origin: *
if serving secured data, else it will leak elsewhere as a caller can send it anywhere.Add credentials back in to CORS requests:
Using "fetch": const response = await fetch(url, { credentials: 'include', });
Using HTML: <img crossorigin="use-credentials" src="" />
.
Response must contain access control allow credentials and origin headers as well as 'Vary: Cookie Origin`!
Requests that browser API's don't generally make are an 'unusual request' from the perspective of cors.
Request type determines 'usual': GET
, HEAD
, or POST
.
Request headers that are 'unusual': Headers not on a 'safelist'.
Headers Access-Control-Request-Method: ...
and Access-Control-Request-Headers: item, item2, ...
with a method of Options
are sent to the destination URL. No credentials are sent in preflight request.
Preflight server response could be happy or not:
happy:
Access-Control-Max-Age: seconds-in-effect
Access-Control-Allow-Methods: unusual-methods-to-allow
Access-Control-Allow-Headers: unusual-headers-to-allow
FORBIDDEN LIST: Browser-controlled list of headers that are always stripped from cors requests.
Preflight response must pass CORS check so status code must be in 200's and response headers must include:
Access-Control-Allow-Origin
Access-Control-Allow-Credentials: true
Remember: Access-Control-Allow-Credentials means the browser is automatically adding credentials to the request not implemented custom headers!
After Preflight response is received then the actual request can be sent.
The post-preflight request must also pass cors tests or it will be forbidden or ignored.
Note: HTTP Method names might be case sensitive, meaning the browser might require Access-Control-Allow-Methods
names to be UPPER-CASED, else they will not pass the check. It is okay to duplicate the method name in Pascal-Case and UPPER-CASE in the same header.
Cookie Header can be considered a Credential!
Cross Origin Resource Sharing on MDN
Web Dev Simplified: CORS in 6 minutes
Return to root README