CORS Communication

CORS is a W3C standard, the full name is "Cross-origin resource sharing". It allows browsers to issue XMLHttpRequest requests to cross-domain servers, thus overcoming the limitation that AJAX can only be used from the same source.

Introduction

CORS needs to be supported by both the browser and the server. Currently, all browsers support this feature.

The entire CORS communication process is done automatically by the browser and does not require user involvement. For developers, CORS communication is no different from ordinary AJAX communication, and the code is exactly the same. Once the browser finds that the AJAX request is cross-domain, it will automatically add some additional header information, and sometimes an additional request will be made, but the user will not perceive it. Therefore, the key to implementing CORS communication is the server. As long as the server implements the CORS interface, it can communicate across domains.

Two requests

CORS requests are divided into two categories: simple requests and not-so-simple requests.

As long as the following two conditions are met at the same time, it is a simple request.

(1) The request method is one of the following three methods.

-HEAD -GET -POST

(2) The HTTP header information does not exceed the following fields.

-Accept -Accept-Language -Content-Language -Last-Event-ID -Content-Type: Limited to three values ​​application/x-www-form-urlencoded, multipart/form-data, text/plain

Anything that does not meet the above two conditions at the same time is a non-simple request. In a word, a simple request is a combination of a simple HTTP method and simple HTTP header information.

The reason for this division is that forms have historically been able to make requests across domains. A simple request is a form request. The browser follows the traditional processing method and does not complicate the behavior, otherwise the developer may switch to using the form to circumvent the restrictions of CORS. For non-simple requests, the browser will adopt a new processing method.

Simple request

Basic process

For simple requests, the browser directly issues CORS requests. Specifically, in the header information, add an Origin field.

The following is an example. The browser finds that this cross-domain AJAX request is a simple request, and automatically adds an Origin field to the header information.

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

In the above header information, the Origin field is used to indicate which domain (protocol + domain name + port) this request comes from. The server decides whether to approve this request based on this value.

If the source specified by Origin is not within the permitted scope, the server will return a normal HTTP response. The browser found that the header of this response did not include the Access-Control-Allow-Origin field (see below for details), and knew that there was an error, so an error was thrown, which was captured by the onerror callback function of XMLHttpRequest. Note that this error cannot be identified by the status code, because the status code of the HTTP response may be 200.

If the domain name specified by Origin is within the permitted range, the response returned by the server will contain several more header fields.

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

In the header information above, there are three fields related to the CORS request, all of which start with Access-Control-.

(1) Access-Control-Allow-Origin

This field is required. Its value is either the value of the Origin field in the request, or a *, which means that the request for any domain name is accepted.

(2) Access-Control-Allow-Credentials

This field is optional. Its value is a Boolean value, which indicates whether to allow the sending of cookies. By default, cookies are not included in CORS requests. Set to true, which means that the server clearly allows the browser to include the cookie in the request and send it to the server together. This value can only be set to true. If the server does not want the browser to send cookies, just not send this field.

(3) Access-Control-Expose-Headers

This field is optional. In CORS request, the getResponseHeader() method of the XMLHttpRequest object can only get the basic fields returned by the six servers: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. If you want to get other fields, you must specify them in Access-Control-Expose-Headers. The above example specifies that getResponseHeader('FooBar') can return the value of the FooBar field.

withCredentials attribute

As mentioned above, the CORS request does not contain Cookie information (and HTTP authentication information, etc.) by default. This is to reduce the risk of CSRF attacks. However, in some cases, the server may need to obtain cookies. In this case, the server needs to explicitly specify the Access-Control-Allow-Credentials field to tell the browser that cookies can be sent.

Access-Control-Allow-Credentials: true

At the same time, the developer must open the withCredentials property in the AJAX request.

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

Otherwise, even if the server requests to send cookies, the browser will not send them. Or, the server requires cookies to be set, and the browser will not process it either.

However, some browsers set the withCredentials property to true by default. This leads to the fact that if the withCredentials setting is omitted, these browsers may still send cookies together. At this time, you can explicitly turn off withCredentials.

xhr.withCredentials = false;

It should be noted that if the server requires the browser to send cookies, Access-Control-Allow-Origin cannot be set as an asterisk, and a clear domain name that is consistent with the requested webpage must be specified. At the same time, cookies still follow the same-origin policy, only cookies set with the server domain name will be uploaded, cookies of other domain names will not be uploaded, and (cross-domain) the document.cookie in the original webpage code cannot read the server domain name. Cookies under.

Non-simple request

Preflight request

Non-simple requests are those that make special requests to the server. For example, the request method is PUT or DELETE, or the type of the Content-Type field is application/json.

For CORS requests that are not simple requests, an HTTP query request will be added before the formal communication, called a "preflight" request (preflight). The browser first asks the server whether the domain name of the current webpage is in the server's permission list, and which HTTP methods and header fields can be used. Only after getting a positive answer, the browser will send a formal XMLHttpRequest request, otherwise it will report an error. This is to prevent these new requests from putting pressure on traditional servers without CORS support, and to give the server an opportunity to reject them in advance. This prevents the server from receiving a large number of DELETE and PUT requests. These traditional forms do not Requests that may be made across domains.

The following is a JavaScript script for the browser.

var url = "http://api.alice.com/cors";
var xhr = new XMLHttpRequest();
xhr.open("PUT", url, true);
xhr.setRequestHeader("X-Custom-Header", "value");
xhr.send();

In the above code, the HTTP request method is PUT, and a custom header information X-Custom-Header is sent.

The browser finds that this is a non-simple request, and automatically sends a "pre-check" request, asking the server to confirm that the request can be made. The following is the HTTP header information of this "preflight" request.

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

The request method used for the "pre-check" request is OPTIONS, which means that this request is for inquiry. In the header information, the key field is Origin, which indicates which source the request comes from.

In addition to the Origin field, the header information of the "preflight" request includes two special fields.

(1) Access-Control-Request-Method

This field is required and used to list which HTTP methods will be used in the browser's CORS request. The above example is PUT.

(2) Access-Control-Request-Headers

This field is a comma-separated string that specifies the additional header fields that the browser will send in CORS requests. The example above is X-Custom-Header.

Response to preflight request

After the server receives the "preflight" request, it checks the Origin, Access-Control-Request-Method and Access-Control-Request-Headers fields, confirms that cross-origin requests are allowed, and can respond.

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

In the above HTTP response, the key is the Access-Control-Allow-Origin field, which means that http://api.bob.com can request data. This field can also be set as an asterisk, which means that any cross-origin request is agreed.

Access-Control-Allow-Origin: *

If the server denies the "preflight" request, it will return a normal HTTP response, but without any CORS-related header fields, or it clearly indicates that the request does not meet the conditions.

OPTIONS http://api.bob.com HTTP/1.1
Status: 200
Access-Control-Allow-Origin: https://notyourdomain.com
Access-Control-Allow-Method: POST

In the server response above, the Access-Control-Allow-Origin field clearly does not include the requesting http://api.bob.com.

At this time, the browser will determine that the server does not agree with the preflight request, so an error is triggered, which is captured by the onerror callback function of the XMLHttpRequest object. The console will print out the following error message.

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

Other CORS-related fields responded by the server are as follows.

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

(1) Access-Control-Allow-Methods

This field is required, and its value is a comma-separated string indicating all cross-domain request methods supported by the server. Note that all supported methods are returned, not just the method requested by the browser. This is to avoid multiple "preflight" requests.

(2) Access-Control-Allow-Headers

If the browser request includes the Access-Control-Request-Headers field, the Access-Control-Allow-Headers field is required. It is also a comma-separated string indicating all header fields supported by the server, not limited to the fields requested by the browser in the "preflight".

(3) Access-Control-Allow-Credentials

This field has the same meaning as in a simple request.

(4) Access-Control-Max-Age

This field is optional and is used to specify the validity period of this pre-check request, in seconds. In the above results, the validity period is 20 days (1728000 seconds), which means that the response is allowed to be cached for 1728000 seconds (20 days). During this period, there is no need to issue another pre-check request.

Normal browser request and response

Once the server passes the "pre-check" request, every subsequent normal CORS request from the browser will be the same as a simple request, with an Origin header field. The server's response will also have an Access-Control-Allow-Origin header field.

The following is the normal CORS request of the browser after the "preflight" request.

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

The Origin field of the above header information is automatically added by the browser.

The following is the normal response of the server.

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

In the above header information, the Access-Control-Allow-Origin field must be included in every response.

Comparison with JSONP

CORS has the same purpose as JSONP, but is more powerful than JSONP. JSONP only supports GET requests, CORS supports all types of HTTP requests. The advantage of JSONP is that it supports older browsers and can request data from websites that do not support CORS.