Cross-Origin Resource Sharing
Circumstances under which is needed, and its benefits
JavaScript can only make calls to URLs that live on the same origin as the location where the script is running (Same-Origin policy).
Cross-Origin Resource Sharing (CORS) is a security mechanism built-into browsers that controls when a web page can interact with resources from a different origin than the one that served the page. This is useful because in JavaScript you bump into CORS when you use fetch, XMLHttpRequest, or other APIs to talk to a backend that lives on another origin or domain.
For example, if a JS app wishes to make an AJAX call to an API running on a different domain, it would be blocked from doing so thanks to the same origin-origin policy.
Most of the time a script running in the user's browser would only ever need to access resources on the same origin. So the fact that JS can't normally access resources on other origins is a good thing for security.
However, there are legitimate scenarios where cross-origin access is desirable or necessary. For example, if you're running a React App that makes calls to an API backend running on a different domain to get web fonts.
//Same-origin policy
https://example.com
//Can only access responses from
https://example.com
//But not from
https://api.other.com
When you call another origin, the browser automatically attaches an origin header to your request
Ex:
//Origin: https://example.com
When a server has been configured to allow cross-origin resource sharing, the response will include CORS headers like Access-Control-Allow-Origin; if not, the browser blocks access and you see a CORS error in the console.
For simple requests like GET, POST or HEAD with standard headers, the browser just sends the request and checks the response headers for
Access-Control-Allow-Origin, which can be a specific origin or domain or '*' to allow any origin.
If the response includes credentials: cookies, HTTP authentication, the server must also send Access-Control-Alow-Credentials.
If a request does not meet the criteria for a simple request, the browser will instead make an automatic preflight request using the OPTIONS method. This call is used to determine the exact capabilities of the server. Only if the the preflight is approved will the browser send the actual request.
An example of such a request might look like this:
//Request
curl -i -X OPTIONS localhost:3000/api/ping \
-H 'Access-Control-Request-Method: GET' \
-H 'Access-Control-Request-Headers: Content-Type, Accept' \
-H 'Origin: http://localhost:3000'
This request basically says: "I would like to make a GET request with the Content-Type and Accept headers from origin. Is that possible?"
The server will include some Access-Control-* headers within the response to indicate whether the request that follows will be allowed or not. These include:
- Access-Control-Allow-Origin: The origin that is allowed to make the request, or * if a request can be made from any origin
- Access-Control-Allow-Methods: A comma-separated list of HTTP methods that are allowed
- Access-Control-Allow-Headers: A comma-separated list of the custom headers that are allowed to be sent
- Access-Control-Max-Age: The maximum duration that the response to the preflight request can be cached before another call is made
The response would then be examined by the browser to decide whether to continue with the request or to abandon it.
So a response to the earlier example might look like this:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
Vary: Access-Control-Request-Headers
Access-Control-Allow-Headers: Content-Type, Accept
Content-Length: 0
Date: Fri, 05 Apr 2019 11:41:08 GMT
Connection: keep-alive
The Access-Control-Allow-Origin header, in this case, allows the request to be made from any origin, while the Access-Control-Allow-Methods header describes only the accepted HTTP methods.
If a given HTTP method is not accepted, it will not appear in this list. In this example, Access-Control-Allow-Headers echos back the headers that were asked for in the OPTIONS request. This indicates that all the requested headers are allowed to be sent.