CORS
Cross-origin resource sharing (CORS) is a browser mechanism which enables controlled access to resources located outside of a given domain.
Same-origin policy (SOP)
The same-origin policy is a web browser security mechanism that aims to prevent websites from attacking each other.
The same-origin policy restricts scripts on one origin from accessing data from another origin.
An origin consists of a URI scheme, domain and port number.
Relaxation of the same-origin policy
The same-origin policy is very restrictive and consequently various approaches have been devised to circumvent the constraints.
Many websites interact with subdomains or third-party sites in a way that requires full cross-origin access.
A controlled relaxation of the same-origin policy is possible using cross-origin resource sharing (CORS).
The cross-origin resource sharing protocol uses a suite of HTTP headers that define trusted web origins and associated properties such as whether authenticated access is permitted. These are combined in a header exchange between a browser and the cross-origin web site that it is trying to access.
How does CORS work?
Simple requests
A simple request is one that meets all the following conditions:
- One of the allowed methods: GET, HEAD, POST
- A CORS safe-listed header is used
- When using the
Content-Type
header, only the following values are allowed:application/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
- No event listeners are registered on any XMLHttpRequestUpload object
- No ReadableStream object is used in the request
Step 1: Client (browser) request
When the browser is making a cross-origin request, the browser adds an Origin header with the current origin (scheme, host, and port).
Step 2: Server response
On the server side, when a server sees this header, and wants to allow access, it needs to add an Access-Control-Allow-Origin header to the response specifying the requesting origin (or * to allow any origin.)
Step 3: Browser receives response
When the browser sees this response with an appropriate Access-Control-Allow-Origin header, the browser allows the response data to be shared with the client site.
Preflighted requests
Unlike simple requests, for "preflighted" requests the browser first sends an HTTP request using the OPTIONS method to the resource on the other origin, in order to determine if the actual request is safe to send. Such cross-origin requests are preflighted since they may have implications for user data.
For example, a client might be asking a server if it would allow a DELETE request, before sending a DELETE request, by using a preflight request:
1 2 3 4 |
|
If the server allows it, then it will respond to the preflight request with an Access-Control-Allow-Methods response header, which lists DELETE:
1 2 3 4 5 |
|
The preflight response can be optionally cached for the requests created in the same URL using Access-Control-Max-Age header like in the above example.
CORS headers
Request headers
Header Name | Example Value | Description | Used in preflight requests | Used in CORS requests |
---|---|---|---|---|
Origin | https://www.test.com | Combination of protocol, domain, and port of the browser tab opened | YES | YES |
Access-Control-Request-Method | POST | For the preflight request, specifies what method the original CORS request will use | YES | NO |
Access-Control-Request-Headers | Authorization, X-PING | For the preflight request, a comma separated list specifying what headers the original CORS request will send | YES | NO |
Response Headers
Header Name | Example Value | Description | Used in preflight requests | Used in CORS requests |
---|---|---|---|---|
Access-Control-Allow-Origin | https://www.test.com | Item3.1 | YES | YES |
Access-Control-Allow-Credentials | true | Item3.2 | YES | YES |
Access-Control-Expose-Headers | Date, X-Device-Id | Column1 | NO | YES |
Access-Control-Max-Age | 600 | Item3.1 | YES | NO |
Access-Control-Allow-Methods | GET, POST, PUT, DELETE | Item3.2 | YES | NO |
Access-Control-Allow-Headers | Authorization, X-PING | Column1 | YES | NO |
Common Pitfalls
- Using * operator for Access-Control-Allow-Origin.
CORS is a relaxation of same-origin policy while attempting to remain secure. Using * disables most security rules of CORS. There are use cases where wildcard is OK such as an open API that integrates into many 3rd party websites.
- Returning multiple domains for Access-Control-Allow-Origin.
Unfortunately, the spec does not allow Access-Control-Allow-Origin: https://mydomain.com, https://www.mydomain.com. The server can only respond with one domain or *, but you can leverage the Origin request header.
- Using wildcard selection like *.mydomain.com.
This is not part of the CORS spec, wildcard can only be used to imply all domains are allowed.
- Not including protocol or non-standard ports.
Access-Control-Allow-Origin: mydomain.com
is not valid since the protocol is not included.
In a similar way, you will have trouble with Access-Control-Allow-Origin: http://localhost
unless the server is actually running on a standard HTTP port like :80.
- Not including Origin in the Vary response header
Most CORS frameworks do this automatically, you must specify to clients that server responses will differ based on the request origin.
- Not specifying the Access-Control-Expose-Headers
If a required header is not included, the CORS request will still pass, but response headers not whitelisted will be hidden from the browser tab. The default response headers always exposed for CORS requests are:
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
-
Pragma
-
Using wildcard when Access-Control-Allow-Credentials is set to true
This is a tricky case that catches many people. If response has Access-Control-Allow-Credentials: true
, then the wildcard operator cannot be used on any of the response headers like Access-Control-Allow-Origin
.