All About CORS Headers
There are already a lot of resources about CORS out there.
I’m going to try and keep this short and sweet — useful tips for web devs.
About CORS
- CORS is a security mechanism based on HTTP headers.
- CORS must be configured on the web server and consumed by the browser.
- It is only relevant for web browsers, not requests from other software such as cURL or a Python script (which will ignore the configuration).
Why Does It Exist?
- You are on a website with
mydomain.com
. - All web browsers have a Same-Origin-Policy (SOP), preventing access to domains
such as
sub.mydomain.com
orotherdomain.com
, as a security measure. - Cross-Origin Resource Sharing (CORS) is a mechanism to allow exceptions to this rule.
When Is It Triggered?
- If the schema, hostname, or port do not match, this is considered cross-origin.
- An example would be
https://mysite.com
andhttps://api.mysite.com
, which are different domains and hence cross-origin. - To avoid CORS for backend APIs, a common approach is to host the API under
a subpath instead
https://mysite.com/api
, and avoid CORS concerns entirely.
Configuring CORS Correctly
Pre-Flight Requests
-
Browsers send a “pre-flight” request under certain conditions before making the actual request (for cross-origin requests).
-
A pre-flight request is a special
OPTIONS
request that checks the browser is allowed to submit to the API before making the actual request.Tip
The most common triggers for this are:
- Using HTTP methods other than
GET
,HEAD
, orPOST
. - Using headers other than simple headers (
Accept
,Accept-Language
,Content-Language
,Content-Type
with specific values). - Using
Content-Type
with values other thanapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
. - Including credentials (
credentials: include
).
- Using HTTP methods other than
-
As above, to avoid CORS and having to send two requests per API call, it’s possible to host an API under a subpath instead.
Tip
If you are using a firewall or proxy, ensure that the
OPTIONS
method is present in Access-Control-Allow-Methods (see further details below).
Access-Control Headers
- These headers control how CORS is enforced by the browser.
- They are set on the web server of the resource you are trying to access, e.g. your NGINX proxy or backend API.
Access-Control-Allow-Origins
- Which origins in the browser can call the API / webserver endpoints.
- Examples:
Access-Control-Allow-Origin: *
(allow all access)Access-Control-Allow-Origin: https://example.com
(specific protocol (https), domain (example.com), and port (443)))
Access-Control-Allow-Credentials
-
If cookies or HTTP basic auth should be sent with cross-origin requests.
-
Example:
Access-Control-Allow-Credentials: true
(allow).Warning
Cannot be set to
true
ifAccess-Control-Allow-Origin: *
.
Access-Control-Allow-Methods
- Specifies which HTTP methods are permitted for cross-origin requests.
- Example:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
.
Access-Control-Expose-Headers
- Specifies which headers the browser can access from the response.
- Example:
Access-Control-Expose-Headers: Content-Length,Content-Range
. - A good example for this is the Content-Disposition which may contain the
original filename of an attachment sent by the server:
- For the filename:
Content-Disposition: attachment; filename="myphoto.jpg"
- Allow the header to be read:
Access-Control-Expose-Headers: Content-Disposition
. - The browser can now download the file with the original filename.
- For the filename:
Other headers
- Cross-Origin-Resource-Policy (CORP): controls how strict the CORS policy is:
same-origin
: Only allow requests from the exact same origin.same-site
: Allow requests from subdomains (e.g. api.example.com for example.com).cross-origin
: Allow all requests from any domain.
- Cross-Origin-Opener-Policy (COOP) & Cross-Origin-Embedder-Policy (COEP): typically used for security and performance optimizations. A common example is usage with the Origin Private File System (OPFS) and SharedArrayBuffer.
- Access-Control-Max-Age: defines how long the results of a pre-flight request can be cached before needing to run again.