Back to homepage

Cross-Origin Resource Sharing (CORS)

Understanding when CORS is needed and its security benefits.

JavaScript can generally only make calls to URLs that reside on the same origin as the script's location. This security restriction is known as the Same-Origin Policy.

Cross-Origin Resource Sharing (CORS) is a browser-based security mechanism that controls how a web page interacts with resources from a different origin. In modern web development, you typically encounter CORS when using the fetch API, XMLHttpRequest, or other tools to communicate with a backend hosted on a different domain or port.

For example, if a JavaScript application attempts to make an AJAX call to an API running on a different domain, the request would be blocked by the Same-Origin Policy unless the server explicitly permits it.

Most of the time, a script running in a user's browser only needs to access resources from its own origin. Therefore, the restriction on cross-origin access is a vital security feature. However, there are legitimate scenarios where cross-origin access is necessary —such as a React app fetching web fonts or data from a dedicated API backend.

//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

If a server is configured for CORS, its response will include specific headers, such as Access-Control-Allow-Origin. If these headers are missing or incorrect, the browser blocks the response, and a CORS error appears in the developer console.

Simple vs Preflight Requests

For simple requests (such as GET, POST, or HEAD using standard headers), the browser sends the request directly and verifies the response headers. The Access-Control-Allow-Origin header may specify a single domain or use * to allow any origin.

If the request includes credentials—such as cookies or HTTP authentication—the server must also include the Access-Control-Allow-Credentials header.

If a request does not meet the "simple" criteria, the browser performs an automatic preflight request using the OPTIONS method. This preliminary call determines the server's capabilities. The actual request is only sent if the preflight is approved.

An example of a preflight request:

//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 essentially asks the server: "I would like to make a GET request using 'Content-Type' and 'Accept' headers from this origin. Is that permitted?"

The server responds with Access-Control-* headers to indicate whether the subsequent request is allowed. Key headers include:

  • Access-Control-Allow-Origin: Specifies which origin can access the resource.
  • Access-Control-Allow-Methods: A comma-separated list of permitted HTTP methods.
  • Access-Control-Allow-Headers: A list of custom headers allowed in the actual request.
  • Access-Control-Max-Age: The duration (in seconds) the preflight response can be cached.

A typical response to the preflight 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 2020 11:41:08 GMT
Connection: keep-alive

In this case, the Access-Control-Allow-Origin: * header allows the request from any origin. The Access-Control-Allow-Headers echoes the requested headers, confirming they are acceptable for the actual request.

CAKE®STACK

Rolling With The Dough