Modern browsers use the Same-Origin Policy (SOP) by default which means that fetching resources from other origins is not allowed. However, in some situations, such operations are necessary. Cross-Origin Resource Sharing (CORS) was designed to address such situations using HTTP response headers, which include Access-Control-Allow-Origin.
What Is Same-Origin Policy
Same-Origin Policy (SOP) is a general web browser security policy for cross-origin requests. It controls access to data between websites and web applications. If there was no SOP, any web page and any JavaScript code would be able to access the DOM of other HTML pages, which would not be favorable for security reasons.
The origin is a set of common characteristics of a web resource. It usually includes three elements: the schema (protocol), the hostname (domain/subdomain), and the port. All resources identified by the same schema:hostname/anything:port have the same origin. However, if even one of the three elements is different, not only if the resources have a different domain, modern browsers such as Google Chrome consider the resources as having a different origin. SOP is applied by the browser every time that elements from different origins interact, for example, a page cannot fetch the content of its iframe unless they are of the same origin.
The above is just a very short introduction to this topic. To learn more about Same-Origin Policy (SOP), read our article on this subject.
How Does CORS Relax the SOP
Cross-origin Resource Sharing works as follows:
- Front-end JavaScript code served from https://example.com (resource 1) attempts to use XmlHttpRequest (XHR) to make an Ajax request for https://css.example.com/formdata/content.json (resource 2).
GET /formdata/content.json HTTP/1.1 Host: css.example.com Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Origin: https://example.com
- The browser receives the response from the web server and sees that the origins are different.
- Normally, due to SOP, the browser would deny resource 1 access to resource 2, for example, return an error Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource.
- If CORS is set up for resource 2, the response from resource 2 includes special HTTP headers, primarily Access-Control-Allow-Origin.
HTTP/1.1 200 OK Age: 277354 Cache-Control: max-age=604800 Content-Encoding: gzip Content-Length: 1701 Content-Type: text/html; charset=UTF-8 Date: Mon, 03 Aug 2020 11:43:26 GMT Vary: Accept-Encoding X-Cache: HIT Access-Control-Allow-Origin: https://example.com
- The Access-Control-Allow-Origin header states that resource 1 is allowed to access resource 2.
- The browser processes the request.
Note that the Access-Control-Allow-Origin header may only specify one source origin or it may specify a wildcard. A wildcard makes resource 2 accessible from all origins. This may, for example, make sense for web fonts, which should be accessible cross-domain.
To enable CORS on your web server, consult the enable-cors website, which contains instructions for nginx, Apache, IIS, and many other web servers.
We also recommend that you watch an excellent video that shows in practice what is the origin, how SOP works, and how CORS makes cross-origin HTTP requests possible.
Preflight Requests
The above method is used for simple requests that the web browser considers safe, for example, typical GET requests. In other cases, for example when there are custom headers in the request (any other header except Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width) or if the HTTP method is PUT, DELETE, CONNECT, OPTIONS, TRACE, or PATCH.
The preflight CORS request is an OPTIONS request with CORS headers:
OPTIONS / HTTP/1.1
Host: www.example.com
(...)
Origin: http://example2.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-CUSTOM, Content-Type
The server response to this OPTIONS method request includes the methods that are allowed, whether it accepts the headers, how to handle credentials, and how long the preflight request is valid:
HTTP/1.1 204 No Content
(...) Access-Control-Allow-Origin: http://example2.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-CUSTOM, Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
After preflight is complete, regular requests with CORS headers may be sent.
Is It Worth It?
Now that you know how CORS works, you may ask yourself: is it worth implementing this on my server? It would mean altering server configuration to add CORS headers for specific resources or altering server-side code to send such headers in the response. Will this make your web application safer?
Unfortunately, SOP and CORS do not prevent any web attacks. They may be treated as an additional mechanism to prevent cross-site request forgery (CSRF) attacks but they must not be used in place of anti-CSRF tokens. Also, SOP and CORS are completely useless in preventing any cross-site scripting (XSS) attacks.
However, because all modern browsers support SOP, you may have to implement CORS headers anyway, just to make it possible for your resources to be used from other origins. This, however, is not a security requirement but a functional requirement – without CORS headers some web applications will simply not work.
Get the latest content on web security
in your inbox each week.