1. What is Cross-Origin
Cross-origin refers to the situation where a browser loads resources from another site that has a different domain (e.g., a.x.com and b.x.com are cross-origin), protocol (http and https are cross-origin), or port (port 80 and port 8080 are cross-origin).
This is contrary to the same-origin policy supported by major JavaScript-enabled browsers. The so-called same-origin policy is a well-known security policy proposed by Netscape.
Currently, all JavaScript-enabled browsers implement this policy. Same-origin means that the domain name, protocol, and port are the same.
For example, the following domains are considered different origins:
http://example.com/http://example.com:8080/http://www.example.com/https://example.com:80/https://example.com/http://example.org/
Because they have different protocols, domain names, or ports
Cross-origin means that the browser cannot execute scripts from other websites. This is caused by the same-origin policy of the browser, which imposes security restrictions on JavaScript.
The same-origin policy restricts the following behaviors:
Cookies, LocalStorage, and IndexDB cannot be read in cross-origin situations
DOM and JS objects cannot be accessed in cross-origin situations
Ajax requests cannot be sent in cross-origin situations
2. Solutions to Cross-Origin Issues
CORS is currently the mainstream solution for cross-origin issues. CORS is a W3C standard, which stands for “Cross-Origin Resource Sharing”.
It allows browsers to make XMLHttpRequest requests to cross-origin servers, thereby overcoming the limitation that AJAX can only be used with the same origin.
Once the browser detects that an AJAX request is cross-origin, it automatically adds some additional header information, and sometimes an additional request is made, but the user will not notice it.
Therefore, the key to implementing CORS communication is the server. As long as the server implements the CORS interface, cross-origin communication can occur.
The browser classifies CORS requests into two categories: simple requests and non-simple requests.
Simple Requests
As long as the following conditions are met, it is considered a simple request:
1. The request method is HEAD, POST, or GET
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)
For simple requests, the browser directly sends the CORS request. Specifically, it adds an Origin field in the header information.
Here is an example where the browser detects that this cross-origin AJAX request is a simple request and automatically adds an Origin field in the header information.
GET /cors HTTP/1.1Origin: http://api.bob.com // The browser automatically adds the Origin field to indicate which origin (protocol + domain + port) this request comes from. The server decides whether to allow this request based on this value.Host: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
If the origin specified in the Origin field is not within the allowed range, the server will return a normal HTTP response. The browser will find that the response header does not contain the Access-Control-Allow-Origin field (see below), and will know that an error has occurred, thus throwing an error that is caught by the XMLHttpRequest’s onerror callback function.
If the domain name specified in the Origin field is within the allowed range, the response returned by the server will include several additional header fields.
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: FooBar
Among the above header information, there are three fields related to CORS requests, all starting with Access-Control-
Access-Control-Allow-Origin
This field is required. Its value must either be the value of the Origin field at the time of the request or a *, indicating acceptance of requests from any domain.
Access-Control-Allow-Credentials
This field is optional. Its value is a boolean indicating whether to allow sending cookies. By default, cookies are not included in CORS requests. Setting it to true indicates that the server explicitly permits cookies to be included in the request. This value can only be set to true; if the server does not want the browser to send cookies, this field can be removed.
Access-Control-Allow-Headers
This field is optional. During CORS requests, the XMLHttpRequest object’s getResponseHeader() method can only access six basic fields: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. If you want to access other fields, you must specify them in Access-Control-Allow-Headers.
The Origin field in the HTTP request header is usually automatically added by the browser when sending cross-origin requests.
If you find that the Origin field is not present in the HTTP request header, it may be due to the following reasons:
1. Non-browser clients
If you are using a non-browser client (such as curl, Postman, Node.js, etc.), these clients do not automatically add the Origin field. You need to manually add this field.
2. Same-origin requests
For same-origin requests (i.e., the domain name, protocol, and port of the request are the same as the current page), the browser does not add the Origin field because these requests are considered safe.
3. CORS requests are disabled
If your request is a cross-origin request but the browser has disabled CORS (Cross-Origin Resource Sharing), then the Origin field may not be added.
Non-Simple Requests
Non-simple requests are those that have special requirements for the server, such as when the request method is PUT or DELETE, or the Content-Type field type is application/json.
Non-simple CORS requests will add an additional HTTP query request before the formal communication, known as a “preflight” request.
The HTTP header information for the “preflight” request.
OPTIONS /cors HTTP/1.1Origin: http://api.bob.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-Header...
The request method for the “preflight” request is OPTIONS, indicating that this request is for inquiry. The key field in the header information is Origin, indicating which origin the request comes from.
In addition to the Origin field, the “preflight” request’s header information includes two special fields.
-
Access-Control-Request-Method: This field is required and lists which HTTP methods the browser’s CORS request will use; in the example, it is PUT.
-
Access-Control-Request-Headers: This field is a comma-separated string that specifies the additional header fields that the browser’s CORS request will send; in the example, it is X-Custom-Header.
Response to the Preflight Request
After the server receives the “preflight” request and checks the Origin, Access-Control-Request-Method, and Access-Control-Request-Headers fields, it can respond if it confirms that cross-origin requests are allowed.
The response header is as follows:
Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-Header...
In the above HTTP response, 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, indicating acceptance of any cross-origin request.
If the browser denies the “preflight” request, it will return a normal HTTP response, but without any CORS-related header fields. At this point, the browser will determine that the server does not agree to the preflight request, thus triggering an error that is caught by the XMLHttpRequest object’s onerror callback function.
Other CORS-related fields in the server’s response are as follows:
- Access-Control-Allow-Methods: GET, POST, PUT // This field is required, and its value is a comma-separated string indicating all the methods supported by the server for cross-origin requests. Note that it returns all supported methods, not just the method requested by the browser. This is to avoid multiple “preflight” requests.
- Access-Control-Allow-Headers: X-Custom-Header // If the browser request includes the Access-Control-Request-Headers field, then 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”.
- Access-Control-Allow-Credentials: true // Whether to allow sending cookies
- Access-Control-Max-Age: 1728000 // This field is optional and specifies the validity period of this preflight request, in seconds. In the above result, the validity period is 20 days (1728000 seconds), meaning that this response can be cached for 1728000 seconds (i.e., 20 days), during which no additional preflight requests need to be made.
Normal Browser Request Response
Once the server passes the “preflight” request, every subsequent normal CORS request from the browser will have an Origin header field, just like a simple request. The server’s response will also always have an Access-Control-Allow-Origin header field.
However, if the browser disables caching (for example, by enabling disable cache in the browser’s F12 network), every non-simple request will still send a “preflight” request. If caching is enabled in the browser, then no preflight request will be sent again.
The response status code for the OPTIONS preflight request is usually 200 or 204.
When handling CORS (Cross-Origin Resource Sharing) preflight requests, the response status code for OPTIONS requests is typically 204 No Content. This is because the main purpose of the preflight request is to obtain CORS-related information from the server, rather than to retrieve actual content. Therefore, the server usually returns a 204 status code, indicating that the request was successful but no content is returned.
HTTP/1.1 204 No Content Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400
Why use 204 No Content?
-
Semantic Compliance: 204 No Content indicates that the request was successful, but the server will not return any content. This aligns with the purpose of the preflight request, as it is only meant to obtain CORS headers, not actual data.
-
Bandwidth Savings: The 204 status code does not return any response body, thus saving bandwidth.
-
Standardization: Most modern browsers and server frameworks follow this standard when handling preflight requests.
Although 204 No Content is the most commonly used response status code, 200 OK can also be used for preflight requests. In this case, the server will return an empty response body, but the status code will still be 200.
HTTP/1.1 200 OKAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Headers: Content-Type, AuthorizationAccess-Control-Max-Age: 86400
Can Access-Control-Max-Age only be used for preflight requests?
Can Access-Control-Max-Age be included in normal request responses?
The Access-Control-Max-Age response header is primarily used for preflight requests (Preflight Request), i.e., OPTIONS requests. Its main function is to inform the browser how long the result of the preflight request can be cached, thereby reducing the number of preflight requests for subsequent requests.
Preflight Requests: Access-Control-Max-Age is mainly used in the response to OPTIONS requests. It tells the browser that the same preflight request does not need to be sent again within the specified time and can directly use the cached result.
Normal Requests: Access-Control-Max-Age is typically not used in normal request responses. If Access-Control-Max-Age is included in a normal request response, the browser will ignore it, as it has no meaning for normal requests.