It allows the browser to issue XMLHttpRequest requests to cross-origin servers, thus overcoming the limitation that AJAX can only be used in the same origin.
This article introduces the internal mechanism of CORS in detail.
(Photo description: taken at Oasis Park in Al Ain, UAE)
1. Introduction
CORS requires both browser and server support. Currently, all browsers support this function, and IE browser cannot be lower than IE10.
The entire CORS communication process is automatically completed by the browser and does not require user participation. For developers, CORS communication is no different from the same origin AJAX communication, and the code is exactly the same. Once the browser discovers that AJAX requests cross-origin, it will automatically add some additional header information, and sometimes there will be an additional request, but the user will not feel it.
Therefore, the key to implementing CORS communication is the server. As long as the server implements the CORS interface, cross-origin communication can be carried out.
Two requests
The browser divides CORS requests into two categories: simple request and not-so-simple request.
As long as the following two major conditions are met at the same time, it is a simple request.
(1) The request method is one of the following three methods:
HEADGETPOST
(2) The header information of HTTP does not exceed the following fields:
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type: Only limited to three values application/x-www-form-urlencoded , multipart/form-data , text/plain
Anyone who does not meet the above two conditions at the same time is a non-simple request.
The browser's handling of these two requests is different.
3. Simple request 3.1 Basic process
For simple requests, the browser directly issues CORS request. Specifically, add an Origin field to the header information.
The following is an example. The browser found that this cross-origin AJAX request was a simple request, and it automatically added an Origin field to the header information.
GET /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
In the header information above, Origin field is used to indicate which source (protocol + domain name + port) the request comes from. Based on this value, the server decides whether to agree to the request.
If the source specified by Origin is not within the permission scope, the server will return a normal HTTP response. The browser found that the header information of this response did not contain the Access-Control-Allow-Origin field (see below for details), so it was known that an error occurred, and thus 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 permission scope, the response returned by the server will have several additional header fields.
Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarContent-Type: text/html; charset=utf-8
Among the header information above, there are three fields related to CORS requests, all starting with Access-Control- .
(1) Access-Control-Allow-Origin
This field is required. Its value is either the value of Origin field at the time of request, or a * , indicating that the request for any domain name is accepted.
(2) Access-Control-Allow-Credentials
This field is optional. Its value is a Boolean value indicating whether to allow the sending of cookies. By default, cookies are not included in CORS requests. Set to true , which means that the server explicitly allows it. Cookies can be included in the request and sent to the server together. This value can only be set to true . If the server does not send cookies by the browser, delete the field.
(3) Access-Control-Expose-Headers
This field is optional. When CORS requests, getResponseHeader() method of XMLHttpRequest object can only get 6 basic fields: 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 FooBar field.
3.2 withCredentials attribute
As mentioned above, CORS requests do not send cookies and HTTP authentication information by default. If you want to send cookies to the server, on the one hand, you need the server to agree and specify the Access-Control-Allow-Credentials field.
Access-Control-Allow-Credentials: true On the other hand, the developer must open the withCredentials property in the AJAX request.
var xhr = new XMLHttpRequest();xhr.withCredentials = true;Otherwise, the browser will not send even if the server agrees to send cookies. Alternatively, the server requires cookies to be set, and the browser will not handle it.
However, if the withCredentials setting is omitted, some browsers will still send cookies together. At this time, you can explicitly close withCredentials .
xhr.withCredentials = false; It should be noted that if you want to send cookies, Access-Control-Allow-Origin cannot be set as an asterisk, and you must specify an explicit domain name that is consistent with the requested webpage. At the same time, cookies still follow the same-origin policy. Only cookies set with server domain names will be uploaded. Cookies from other domain names will not be uploaded, and document.cookie in the original (cross-original) web page code cannot read cookies under the server domain name.
4. Non-simple request 4.1 Pre-flight request
A non-simple request is a request that has special requirements for the server, such as the request method is PUT or DELETE , or Content-Type field type is application/json .
A CORS request that is not a simple request will add an HTTP query request before the official communication, which is called a "preflight" request.
The browser first asks the server whether the domain name on which the current web page is located is on the server's license list, and which HTTP verbs and header fields can be used. Only when a positive reply is received will the browser issue a formal XMLHttpRequest request, otherwise an error will be reported.
Below is a browser JavaScript script.
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 sends a custom header information X-Custom-Header .
The browser found that this was a non-simple request, so it automatically issued a "pre-flight" request, requiring the server to confirm that this can be requested. Below is the HTTP header information for this "preflight" request.
OPTIONS /cors HTTP/1.1Origin: http://api.bob.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-HeaderHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0... The request method used by the "preflight" request is OPTIONS , indicating that this request is used to inquire. In the header information, the key field is Origin , indicating 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 to list which HTTP methods will be used for 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 information field that the browser CORS request will send. The above example is X-Custom-Header .
4.2 Response to pre-flight requests
After the server receives the "preflight" request, after checking Origin , Access-Control-Request-Method and Access-Control-Request-Headers fields, confirm that cross-origin requests are allowed, and you can respond.
HTTP/1.1 200 OKDate: Mon, 01 Dec 2008 01:15:39 GMTServer: Apache/2.0.61 (Unix)Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderContent-Type: text/html; charset=utf-8Content-Encoding: gzipContent-Length: 0Keep-Alive: timeout=2, max=100Connection: Keep-AliveContent-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 to an asterisk to agree to any cross-origin request.
Access-Control-Allow-Origin: *
If the browser negates the "preflight" request, a normal HTTP response will be returned, but there are no CORS-related header fields. At this time, the browser will determine that the server does not agree to the preflight request, so an error is triggered and captured by the onerror callback function of 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 that the server responds to are as follows.
Access-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderAccess-Control-Allow-Credentials: trueAccess-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 one requested by the browser. This is to avoid multiple "preflight" requests.
(2) Access-Control-Allow-Headers
If the browser request includes Access-Control-Request-Headers field, Access-Control-Allow-Headers field is required. It is also a comma-separated string indicating that all header fields supported by the server are not limited to fields requested by the browser in "Preflight".
(3) Access-Control-Allow-Credentials
This field has the same meaning as it is when requested in a simple way.
(4) Access-Control-Max-Age
This field is optional and is used to specify the validity period of this preflight request, in seconds. In the above results, the validity period is 20 days (1728,000 seconds), which means that the response is cached for 1728,000 seconds (i.e. 20 days). During this period, no other preflight request is required.
4.3 Normal requests and responses from the browser
Once the server passes the "preflight" request, every time the browser's normal CORS request is the same as a simple request, there will be an Origin header information field. The server's response will also have an Access-Control-Allow-Origin header field.
Below is the normal CORS request of the browser after the "preflight" request.
PUT /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comX-Custom-Header: valueAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
The Origin field of the above header information is automatically added by the browser.
Below is the normal response from the server.
Access-Control-Allow-Origin: http://api.bob.comContent-Type: text/html; charset=utf-8
In the header information above, the Access-Control-Allow-Origin field must be included in each response.
5. Comparison with JSONP
CORS is used in the same purpose as JSONP, but it 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 old-fashioned browsers and can request data from websites that do not support CORS.
(over)