sharing(CORS)

Cross-origin resource sharing (CORS) is a mechanism for making restricted resources of a web page accessible to pages from other domains based on HTTP headers. Through this mechanism, pages can freely use images, styles, scripts, iframes, and videos from different sources (cross-origin).

In general, some cross-origin requests (especially ajax) are prohibited by the same-origin policy. CORS defines a way to allow web application servers to perform cross-origin access control, thus enabling secure cross-origin data transfer.

CORS is currently supported by almost all modern browsers and can be found at MDN for information on browser compatibility.

Browsers divide CORS requests into two categories: simple requests and not-so-simple requests. These two terms are not part of the Fetch specification.

Simple Requests

Some requests do not trigger a CORS preflight request. Such requests are referred to as “simple requests” in this document.

Definition

A request is considered a “simple request” if it meets all of the following conditions: 1.

  1. the request method is HEAD, GET or POST.

  2. HTTP headers are allowed to contain only the set of CORS-safe initial fields defined by the Fetch specification, except for those fields that are automatically set by the user agent and those defined in the Fetch specification as disabled header names.

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type is limited to three values: application/x-www-form-urlencoded, multipart/form-data or text/plain.
  3. Any XMLHttpRequestUpload object in the request does not have any event listeners registered; the XMLHttpRequestUpload object can be accessed using the XMLHttpRequest.upload property.

  4. No ReadableStream object is used in the request.

Simple requests are designed to be compatible with forms, which have historically been able to make cross-domain requests.

Basic flow

For simple requests, the browser makes a CORS request directly. Specifically, a field named Origin is added to the HTTP header.

1
2
3
4
5
6
GET /cors HTTP/1.1
Origin: http://foo.example
Host: foo.example
Accept-Language: zh-CN
Connection: keep-alive
User-Agent: Mozilla/5.0...

In the above header, the Origin field is used to indicate the source of this request, and the server side decides whether to grant the request based on this value.

If the source specified by Origin is within the permitted range, the response header returned by the server will add the following fields.

  • The Access-Control-Allow-Origin field indicates the source of the request allowed by the server, and its value is either the value of the Origin field at the time of the request or *.
  • The Access-Control-Allow-Credentials field indicates whether the server is allowed to send credential information; this field is optional, and by default, no credential information is allowed.
  • The Access-Control-Expose-Headers field indicates the allowed HTTP header fields specified by the server, which is optional.

If the source specified by Origin is not in the permitted range, the server returns a normal HTTP response without the Access-Control-Allow-Origin field. When the browser finds that this field is not included it knows that the request is in error and will throw an exception. Note that the HTTP response code for this error may be 200 or 204, and therefore cannot be identified by the status code.

Code example

1
2
3
fetch('https://baoshuo.ren', {
  mode: 'no-cors',
});

Non-simple requests - Pre-check requests

As mentioned above, CORS requests have non-simple requests in addition to simple requests. In short, non-simple requests are requests that have special requirements for the server, such as a request method of PUT or DELETE, or the value of the Content-Type field in the HTTP header is not one of the three “CORS-safe Content-type field values” described above.

Basic flow

CORS requests that are not simple requests add an HTTP query request called a “preflight” before the formal communication.

CORS Basic flow

As you can see from the above message, the browser first sends a “preflight request” using the OPTIONS method, which is a method defined in the HTTP/1.1 protocol to obtain more information from the server. This method has no impact on the server resources. The preflight request carries both of the following prefix fields.

1
2
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
  • The Access-Control-Request-Method field will inform the server of the method that will be used for the actual request.
  • The Access-Control-Request-Headers field will inform the server of the custom request header fields that will be carried by the actual request.

The server will decide accordingly whether to allow the actual request and return the appropriate response.

1
2
3
4
5
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
  • The Access-Control-Allow-Origin field does not differ from when a simple request is made.
  • The Access-Control-Allow-Methods field indicates which methods the server allows to initiate requests.
  • The Access-Control-Allow-Headers field indicates the additional fields the server allows to be carried in the request headers.
  • The Access-Control-Allow-Credentials field does not differ from when a simple request is made.
  • The Access-Control-Max-Age field indicates the validity time of the response, during which the browser is not required to initiate another preflight request for the same request. Note that the browser itself maintains a maximum validity time, and if the value of this field exceeds the maximum validity time maintained by the browser, it will not take effect.

If the server “denies” a preflight request, it also returns a normal HTTP response, but does not contain any HTTP header fields related to CORS. At this point the browser assumes that the server did not agree to the preflight request and throws an error.

Once the preflight request is passed, the next steps are the same as for a simple request, so I won’t go over them here.

Code example

1
2
3
fetch('https://baoshuo.ren', {
  mode: 'cors',
});

CORS requests with identity credentials

As mentioned above, CORS requests do not send credential information (cookies and HTTP authentication information) by default. To send credentials to the server, not only does the server need to specify the Access-Control-Allow-Credentials field in the HTTP header, but it also needs to specify whether to send credential information when requesting.

Code example

The withCredentials flag needs to be set to true when making a CORS request to the server using XmlHttpRequest.

1
2
3
4
5
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://baoshuo.ren', true);
xhr.withCredentials = true;
xhr.onreadystatechange = handler; // The Handler here needs to be defined by yourself
xhr.send();

Requests made with fetch require credentials to be set to include in order for the browser to send a request with credentials to a cross-domain source.

1
2
3
fetch('https://baoshuo.ren', {
  credentials: 'include',
});

Comparison with JSONP

CORS and JSONP are used for the same purpose, but CORS is more powerful than JSONP.

The disadvantage of JSONP is that it only supports GET requests, while CORS supports all types of HTTP requests. If a site needs to be compatible with older browsers or needs to request data from sites that do not support CORS, you still need to use JSONP.