Cross-Origin Resource Sharing (CORS)

Cross-origin refer to URI with different protocol, host, OR port.

For security reasons, browsers block cross-origin HTTP requests from scripts. To allow this, the cross-origin server being hit must send Cross-Origin-Allow-Origin response header.

How it works

In general, the browser will send a "preflight" OPTION request to the origin server, and upon the cross-origin server's approval, proceeds to send the actual request.

There are, however, requests that are considered "simple requests" that do not require sending the preflight request (see: simple requests).

Simple request CORS flow

The browser simply sends the actual request without a preflight request.

Simple CORS flow
Simple CORS flow (Source: MDN)

Preflighted request CORS flow

The browser sends a preflight request before sending the actual request. Note the use of the following request headers:

  • Access-Control-Request-Method (specifying the HTTP request method)
  • Access-Control-Request-Headers (specifiying list of request headers in the actual request)
Preflighted CORS flow
Preflighted CORS flow (Source: MDN)

Sending credentials with CORS

By default, for cross-origin requests, the browser will not send credentials (e.g. cookies and HTTP Authentication information).

If you want to send credentials, you have to set a special flag on the XMLHttpRequest object or the Request constructor (such as the withCredentials property below).

// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';

function callOtherDomain() {
  if (invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true; // <-
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

Expected server response: For the request to be successful AND for the response to be given to JavaScript, the cross-origin server must send the Access-Control-Allow-Credentials: true response header, AND the Access-Control-Allow-Origin response header must not be *.

Note that third-party cookie policies (e.g. SameSite cookie attribute or third-party cookie browser settings) will still apply.

Response headers

Access-Control-Allow-Origin

Enables CORS on the specified origin (or any origin if * is given).

Access-Control-Allow-Origin: <origin>
Access-Control-Allow-Origin: *

Dynamic origin: You can only specify a single origin. If the Access-Control-Allow-Origin value changes dynamically based on the request origin, you should also include Vary: Origin response header.

Access-Control-Allow-Credentials

For a preflight request: indicates if the actual request can be made using credentials. (If omitted, it will fail and the response would not be given to JavaScript.)

For an actual request: tells the browser whether to expose the response to JavaScript.

The only valid value is true. To set false, omit this header entirely.

Access-Control-Allow-Credentials: true

Access-Control-Allow-Methods

Lists allowed HTTP methods on the resource (URI).

* means allow all.

Access-Control-Allow-Methods: <method>, <method>, ...

Access-Control-Allow-Headers

Indicates which HTTP headers can be used when making the actual request.

* means allow all.

Access-Control-Allow-Headers: <header-name>, <header-name>, ...

Access-Control-Expose-Headers

Allows JavaScript to read the given response headers. (Only CORS-safelisted response headers are exposed by default.)

Access-Control-Expose-Headers: <header-name>, <header-name>, ...

Access-Control-Max-Age

Indicates how long (in seconds) response to a preflight request can be cached. (Referring to the Access-Control-Allow-Methods and Access-Control-Allow-Headers response headers).

E.g. Access-Control-Max-Age: 600.

Access-Control-Max-Age: <delta-seconds>

Request headers

Origin

Marks from which URI the request was sent (it can be null).

Access-Control-Request-Method

In the preflight request: specifies which HTTP method is to be used in the actual request (e.g. POST).

Access-Control-Request-Headers

In the preflight request: specifies which request headers are to be included in the actual request.

The server "response" would be on the Access-Control-Allow-Headers response header.

Access-Control-Request-Headers: <header-name>, <header-name>, ...

References