preface

CORS is one of the most commonly used cross-domain methods in addition to JSONP, and it is also one of the essential points for interview. CORS is a W3C standard, which stands for Cross-Origin Resource Sharing. It allows browsers to make XMLHttpRequest requests to cross-source servers, overcoming the limitation that AJAX can only be used from the same source. Only using CORS is not enough for us. We need to know how and more about why. The error is shown below:

Error using FETCH:

What is cross-domain

Before we talk about CORS, we need to understand what is cross-domain. The cornerstone of browser security is the same-origin policy. Homologous refers to “three identical “:

  • The agreement is the same
  • Domain name is the same
  • The same port is used as an example:http://www.example.com
http://www.example.com/a.html homologous http://example.com domain different (different) http://www.example.com:8081 port (80 by default) https://www.example.com Different protocols (different sources)Copy the code

The purpose of the same-origin policy is to ensure the security of user information and prevent malicious websites from stealing data.

Limitation: If not homologous, there are three behaviors that are restricted.

  • Cookie,LocalStorage,IndexDBUnable to read.
  • DOMNot available.
  • AJAXThe request could not be sent.

CORS principle analysis

CORS is a fundamental solution to cross-source AJAX requests. JSONP can only make GET requests, but CORS allows any type of request.

The entire CORS communication process is completed automatically by the browser, without user participation. For developers, CORS communication is no different from the same source AJAX communication; the code is exactly the same. Once the browser realizes that the AJAX request is cross-source, it automatically adds some additional header information and sometimes an additional request, but the user doesn’t notice. Therefore, the key to CORS communication is the server. As long as the server implements the CORS interface, it can communicate across sources.

One or two requests

Browsers divide CORS requests into two categories: simple requests and not-so-simple requests. As long as the following two conditions are met, it is a simple request.

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

    HEAD
    GET
    POST
Copy the code

(2) The Request Headers should 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/plainCopy the code

If the above two conditions are not met at the same time, it is a non-simple request.

The browser handles these two requests differently.

Two, simple request

  1. For simple requests, the browser issues them directlyCORSThe request. Specifically, add one to the headerOriginField.

Here is an example where the browser realizes that the cross-source AJAX request is a simple request and automatically adds an Origin field to the header.

GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-language: EN-US Connection: Keep alive - the user-agent: Mozilla / 5.0...Copy the code

As shown :(with the Origin field added)

Origin

If Origin specifies a source that is not within the scope of the license, the server will return a normal HTTP response. When the browser finds that the response header does not contain the access-control-allow-Origin field (see below), it knows something is wrong and raises an error that is caught by the XMLHttpRequest’s onError callback. Note that this error cannot be identified by the status code, because the status code of the HTTP response could be 200.

If the Origin domain name is within the scope of the license, the server will return a response with 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
Copy the code

In the above header, there are three fields related to the CORS request, all beginning with access-control -. (1) access-control-allow-origin This field is required. Its value is either the value of the Origin field at the time of the request, or an * to accept requests for any domain name.

(2) Access-control-allow-Credentials This field is optional. Its value is a Boolean value indicating whether cookies are allowed to be sent. By default, cookies are not included in CORS requests. Set to true, indicating that the server explicitly permits the Cookie to be included in the request and sent to the server. This value can only be set to true if the server does not want the browser to send cookies to delete this field. In the cross-domain case, not only do you set withCredentials on the front end, but you also set access-control-allow-Credentials on the back end. The Response Headers in the request have access-control-allow-Credentials: true

(3) Access-control-exposure-headers This field is optional. When CORS requests, the XMLHttpRequest object’s getResponseHeader() method gets only six basic fields: Cache-control, content-language, Content-type, Expires, last-modified, and Pragma. If you want to get other fields, you must specify them in access-Control-exposure-headers. The example above specifies that getResponseHeader(‘FooBar’) can return the value of the FooBar field.

2. The withCredentials attribute

As mentioned above, CORS requests do not send cookies or HTTP authentication information by default. If you want to send cookies to the server, on the one hand, the server agrees to specify the access-control-allow-credentials field.

Access-Control-Allow-Credentials: true
Copy the code

On the other hand, the developer must open the withCredentials property in the AJAX request.

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
Copy the code

Otherwise, even if the server agrees to send a Cookie, the browser will not send it. Or, if the server asks for a Cookie, the browser won’t handle it.

However, if you omit the withCredentials setting, some browsers will still send cookies together. In this case, you can explicitly disable withCredentials.

xhr.withCredentials = false;
Copy the code

Note that if you want to send cookies, access-control-allow-origin cannot be set to an asterisk and must specify an explicit domain name that is consistent with the requested web page. At the same time, cookies still follow the same-origin policy, only the cookies set by the server domain name will be uploaded, the cookies of other domain names will not be uploaded, and (cross-source) the document. Cookie in the original web code can not read the cookies under the server domain name.

Redentials (User credentials): Value cookies, HTTP authentication, and TLS client certificates, which do not involve proxy authentication or source headers. The default value is false. Setting withCredentials when obtaining the same source resource has no impact.

Three, non-simple request

  1. Preview the request

That means the browser sends two HTTP requests. Request Method: OPTIONS the first time, and the second time.

Non-simple requests are requests that have specific requirements for the server, such as the request method being PUT or DELETE, or the content-type field being application/ JSON.

For CORS requests that are not simple requests, an HTTP query request is added before the formal communication, called a preflight request.

Common errors are as follows:

This means that the request is not approved in advance, and the request is no longer formally sent
token
options
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token,Token")
The header contains custom fields. The browser sends an options request first. If the request is approved, the browser continues to send a formal post request.

The browser first asks the server if the domain name of the current web page is on the server’s licensed list, as well as what HTTP verbs and header fields to use. The browser sends a formal XMLHttpRequest request only if it receives a positive response, otherwise it reports an error.

The browser first asks the server, and the Request Method is OPTIONS, as shown in the figure:

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

In the above code, the HTTP request method is PUT and sends a Custom Header x-custom Header.

When the browser realizes that this is a non-simple request, it automatically issues a “precheck” request, asking the server to confirm that the request can be made. Here is the HTTP header for the precheck 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...Copy the code

The precheck request uses a request method of OPTIONS, which indicates that the request is intended to be asked. In the header, the key field is Origin, which indicates the source from which the request came. In addition to the Origin field, the precheck request header contains two special fields.

(1) access-control-request-method this field is required to list which HTTP methods are used for CORS requests.

(2) Access – Control – Request – Headers

This field is a comma-separated string that specifies the additional Header field that will be sent for browser CORS requests, such as x-custom Header in the previous example.

  1. Response to precheck request

After the server receives the “precheck” Request, it checks the Origin, access-control-request-method, and Access-control-Request-headers fields, confirms that cross-source requests are allowed, and then responds.

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
Copy the code

In the HTTP response above, the key is the access-control-allow-Origin field, which indicates that http://api.bob.com can request data. This field can also be set to an asterisk to agree to any cross-source request.

Access-Control-Allow-Origin: *
Copy the code

If the browser denies the precheck request, it will return a normal HTTP response, but without any CORS related header fields. At this point, the browser determines that the server did not agree to the precheck request and therefore fires an error that is caught by the XMLHttpRequest object’s onError callback. The console will print the following error message.

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

The server responded to other CORS related fields 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
Copy the code

(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 precheck 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 fields requested by the browser in precheck.

(3) the Access – Control – Allow – Credentials

This field has the same meaning as a simple request.

(4) the Access – Control – Max – Age

This field is optional and specifies the validity period of the precheck request. The unit is second. In the above result, the validity period is 20 days (1,728,000 seconds), which allows the response to be cached for 1,728,000 seconds (20 days) without another precheck request being issued.

  1. Normal browser requests and responses

Once the server passes the “precheck” request, every normal BROWSER CORS request will have the same Origin header field as a simple request. The server’s response also has an access-control-allow-Origin header field.

Below is the normal CORS request for the browser after the “precheck” 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...Copy the code

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

Here is the normal response from the server.

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

In the preceding header, the access-control-allow-Origin field is always included in each response.

Set the withCredentials flag of XMLHttpRequest to true to send Cookies to the server. Because this is a simple GET request, the browser does not “precheck” it. However, if the response on the server side does not carry access-control-allow-credentials: true, the browser will not return the response content to the sender of the request.

The server cannot set the value of access-control-allow-Origin to * for requests with identity credentials. This is because the header of the request carries Cookie information. If the access-control-allow-Origin value is”The request will fail. Set the value of access-control-allow-Origin tofoo.example, the request will execute successfully. If you set it to, the following error will be reported:

  • When will the browser send a simple request? When will a browser send a non-simple request?

The cross-domain resource sharing standard adds a new set of HTTP header fields that allow servers to declare which sources have access to which resources through the browser. In addition, the specification requires that HTTP request methods that may adversely affect server data (especially HTTP requests other than GET, or POST requests with certain MIME types), The browser must first make a Preflight request using the OPTIONS method to know whether the server will allow the cross-domain request. After the server acknowledges permission, the actual HTTP request is made. In the return of the precheck request, the server can also notify the client whether it needs to carry credentials (including Cookies and HTTP authentication-related data).

Some requests do not trigger CORS precheck requests. A request may be considered a “simple request” if all of the following conditions are met:

1. Use one of the following methods:

GET
HEAD
POST
Copy the code

2. The Fetch specification defines a set of header fields that are safe for CORS. No header fields outside this set can be set manually. The set is:

Accept accept-language content-language content-type DPR Downlink save-data viewport-width WidthCopy the code

The value of content-type is limited to one of the following:

text/plain
multipart/form-data
application/x-www-form-urlencoded
Copy the code

In the following image, the radical field is set because the Request Header contains access-control-Request-headers: token. This triggers the precheck request.

Comparison with JSONP

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

JSONP supports only GET requests, while CORS supports all types of HTTP requests. The advantages of JSONP are support for older browsers and the ability to request data from sites that do not support CORS.

Code demo

In the case of a cross-domain request, the browser’s console will report the following error:

In this case, you need to set the server (nodejs as an example).

const http = require('http')
http.createServer(function (request, response) {
    console.log('request come', request.url)
    
    response.writeHead(200, {
      'Access-Control-Allow-Origin': The '*'
    })
    response.end('123')
}).listen(8080)

Copy the code

Return as follows:

You can see that the cross-domain request was successfully accepted.

Key points:

Whether we set access-control-allow-Origin: * or not, the browser sends a request to our server and receives the return. When the browser parses, it finds that there is no access-control-allow-Origin: * header in Response Headers. If the Response Headers is set to Allow, the browser will ignore the content returned by the request and report a cross-domain error in the command line. This is a feature provided by the browser.

Access-control-allow-origin: * is not safe because it allows Access to all domains, so we can set a specific domain to Access. For example, access-control-allow-origin: http://www.baidu.com. In this case, the browser can parse the domain name only when the domain name http://www.baidu.com is accessed.

conclusion

Cross-domain problem is a problem that every front-end developer will encounter. The cross-domain method includes not only Jsonp and CORS, but also postMessage, webScorket and so on. When we encounter cross-domain problem, we can quickly locate the problem and know its cause, which is the essential quality for every programmer. I hope you found this article helpful.

Reference: HTTP Access Control (CORS) Headers