Understanding Cross-Origin Resource Sharing (CORS)
Summary
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that allows controlled cross-origin HTTP requests via specific headers. This guide explains CORS headers, requests, server setup, and debugging common errors.
Introduction #
Cross-Origin Resource Sharing (CORS) is a mechanism defined in the Fetch standard that allows web applications to access resources hosted on a different origin than their own. It enables a server to indicate whether the browser should permit a web page running at one origin to access resources located at another origin.
Modern web browsers implement the Same-Origin Policy, a security feature that restricts how a document or script loaded from one origin can interact with resources from another origin. CORS relaxes this policy in a controlled way through a set of HTTP headers.
Specifications #
CORS is defined in the Fetch standard by the Web Hypertext Application Technology Working Group (WHATWG). It builds on the origin concept as defined in RFC 6454. An “origin” consists of the scheme (such as http or https), host, and port.
CORS involves browser behavior, HTTP request and response headers, and server logic. The server must opt into sharing by including specific headers in the response. If the server does not allow the origin, the browser blocks access and logs a CORS error in the developer tools console.
Quick reference #
| Header | Use Case |
|---|---|
Access-Control-Allow-Origin | Permit access to the resource |
Access-Control-Allow-Methods | Allow specific HTTP methods |
Access-Control-Allow-Headers | Allow custom request headers |
Access-Control-Allow-Credentials | Allow cookies and credentials |
Access-Control-Expose-Headers | Expose non-standard headers |
Access-Control-Max-Age | Cache preflight responses |
Origin | Sent with all cross-origin requests |
Access-Control-Request-Method | Sent in preflight to specify HTTP method |
Access-Control-Request-Headers | Sent in preflight to specify headers |
Vary: Origin | Informs cache of per-origin variation |
Requests that use CORS #
CORS applies whenever a web application makes a request to a different origin. These are categorized into three types.
Simple requests #
A request is considered simple if it satisfies all of the following:
- Uses one of these HTTP methods:
GET,HEAD, orPOST. - Does not include any custom headers.
- If using
POST, theContent-Typemust beapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain.
Example:
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data));
This is a simple GET request with no custom headers. If the server includes Access-Control-Allow-Origin, the browser permits access.
Preflight requests #
Requests using methods like PUT, PATCH, DELETE, or those with custom headers, trigger a preflight OPTIONS request. This is a browser-initiated check before the actual request is made.
Example:
fetch("https://api.example.com/update", {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ id: 1, value: "new" })
});
The browser first sends an OPTIONS request with headers like Access-Control-Request-Method and Access-Control-Request-Headers. The server must respond with the allowed methods and headers.
Credentialed requests #
These are requests that include cookies, HTTP authentication, or TLS client certificates.
Example:
fetch("https://api.example.com/profile", {
credentials: "include"
});
The server must respond with both Access-Control-Allow-Origin and Access-Control-Allow-Credentials: true. Wildcard origins (*) are not allowed in this case.
Functional overview #
- A JavaScript client initiates a request to another origin.
- The browser checks whether the request qualifies as simple.
- If not, it sends a preflight
OPTIONSrequest. - The server responds with CORS headers.
- The browser evaluates the headers and proceeds or blocks the request.
CORS is enforced by browsers. If the server does not explicitly allow access, the response is blocked even if the network connection succeeds.
Use cases #
Single-page applications consuming APIs A React application hosted at
https://frontend.example.comneeds to callhttps://api.example.com.Loading fonts and media from CDNs A site hosted at
example.comuses a font fromfonts.cdnprovider.net.Third-party services integration Payment gateways, analytics, and maps APIs often require CORS to work.
Administrative dashboards Tools hosted on
admin.example.comrequest resources from internal APIs.
HTTP response headers #
Access-Control-Allow-Origin #
Used to indicate which origin is allowed to access the resource.
Example:
Access-Control-Allow-Origin: https://frontend.example.com
Only the specified origin is allowed to read the response. Wildcards (*) can be used unless credentials are included.
Access-Control-Allow-Methods #
Specifies the HTTP methods allowed during a preflight request.
Example:
Access-Control-Allow-Methods: GET, POST, PUT
This tells the browser which methods are permitted in the actual request.
Access-Control-Allow-Headers #
Specifies which custom headers can be used in the actual request.
Example:
Access-Control-Allow-Headers: Authorization, Content-Type
The browser will block requests containing headers not listed here.
Access-Control-Allow-Credentials #
Indicates whether the browser should include credentials.
Example:
Access-Control-Allow-Credentials: true
This must be set when cookies or authentication are included. Wildcard origins are not allowed in this case.
Access-Control-Expose-Headers #
Lists headers that the browser can access from the response.
Example:
Access-Control-Expose-Headers: X-Custom-Header, X-RateLimit-Limit
By default, the browser only exposes a limited set of safe headers.
Access-Control-Max-Age #
Defines how long the results of a preflight request can be cached.
Example:
Access-Control-Max-Age: 86400
This reduces the number of preflight requests by caching the permissions for 24 hours.
Vary: Origin #
Indicates that the response varies based on the Origin header.
Example:
Vary: Origin
This is important for caching behavior on CDN and proxy servers to avoid serving one origin’s response to another.
HTTP request headers #
Origin #
Indicates the origin of the request.
Example:
Origin: https://frontend.example.com
The server uses this to determine whether to allow access.
Access-Control-Request-Method #
Used in preflight requests to indicate the HTTP method of the actual request.
Example:
Access-Control-Request-Method: PUT
Tells the server which method the browser intends to use.
Access-Control-Request-Headers #
Used in preflight to list custom headers.
Example:
Access-Control-Request-Headers: Authorization, X-Custom-Header
The server must include these in Access-Control-Allow-Headers to permit them.
Server-side implementation examples #
Node.js with Express #
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://frontend.example.com',
credentials: true
}));
app.listen(3000);
Flask (Python) #
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins="https://frontend.example.com", supports_credentials=True)
Django #
# settings.py
CORS_ALLOWED_ORIGINS = [
"https://frontend.example.com"
]
CORS_ALLOW_CREDENTIALS = True
Nginx #
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://frontend.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
add_header 'Access-Control-Allow-Credentials' 'true';
}
Apache #
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://frontend.example.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS"
Header set Access-Control-Allow-Headers "Authorization, Content-Type"
Header set Access-Control-Allow-Credentials "true"
</IfModule>
Error handling and debugging #
When CORS-related issues occur, browsers enforce strict client-side blocking, often without sending the full response data to the JavaScript environment. This can make diagnosing problems confusing, especially when the request reaches the server but the browser still fails to deliver the data to your application.
CORS errors are typically surfaced as network failures in JavaScript, accompanied by descriptive messages in the browser console. Since these errors originate from the browser’s security layer, they cannot be bypassed or caught by typical error-handling code like try/catch in JavaScript.
Debugging tools #
- Browser Developer Tools:
Use the Network tab to inspect HTTP requests and responses. Check the presence and correctness of CORS headers. - Console Logs:
The browser console logs clear error messages when CORS fails. These logs often indicate the nature of the failure and point to missing or misconfigured headers. Command-line tools:
Use tools likecurlto emulate CORS requests and inspect server responses without browser enforcement. Example:curl -X OPTIONS -H "Origin: https://example.com" -H "Access-Control-Request-Method: POST" https://api.example.com- Postman:
Postman allows you to craft HTTP requests with custom headers and inspect the full response. You can simulate preflightOPTIONSrequests and check for proper header configuration. Note that Postman itself does not enforce browser CORS restrictions, so use it to verify server behavior, not client enforcement.
Common CORS errors #
No 'Access-Control-Allow-Origin' header:
The server did not include the required header in its response. Without it, the browser blocks access to the resource.- Preflight request fails:
The server did not respond correctly to anOPTIONSrequest, or it is not configured to handle it. This often happens when required headers likeAccess-Control-Allow-MethodsorAccess-Control-Allow-Headersare missing. Access-Control-Allow-Credentialsused with wildcard origin:
CORS forbids usingAccess-Control-Allow-Credentials: truein combination withAccess-Control-Allow-Origin: *. The server must specify an explicit origin instead.
Real-world pitfalls #
- Redirects can silently break CORS by losing headers.
- Mixing
httpandhttpscounts as cross-origin. - Using non-standard ports (like
8080instead of80) triggers CORS. - Some browsers enforce stricter CORS checks in private or incognito modes.
XMLHttpRequestdiffers fromFetch APIin its default credential handling (same-originvs.omit).- Allowing
Access-Control-Allow-Origin: *with credentials is a critical misconfiguration. - Preflight requests introduce latency. Use
Access-Control-Max-Ageto reduce them. Minimize custom headers to avoid triggering preflights.
FAQ's #
Most common questions and brief, easy-to-understand answers on the topic:
What does Cross-Origin Resource Sharing (CORS) do?
CORS allows web applications to request resources from a domain different from the one that served the original page using specific HTTP headers.
Why does the browser block cross-origin requests?
Browsers enforce the Same-Origin Policy to prevent malicious sites from reading sensitive data from another origin without permission.
What is a preflight request?
A preflight request is an initial request sent by the browser using the HTTP OPTIONS method to determine if the actual request is safe to send.
Can CORS be configured on the client side?
No, CORS is a server-side configuration; clients can only send requests, while the server must respond with the correct headers.
What header is required to allow cross-origin requests?
The Access-Control-Allow-Origin header must be included in the server response to permit cross-origin requests.
Further readings #
Sources and recommended, further resources on the topic:
- MDN Web Docs: Cross-Origin Resource Sharing (CORS)
- WHATWG: Fetch Standard
- IETF: RFC 6454 - The Web Origin Concept
- WHATWG: HTML Living Standard - Origin
License
Understanding Cross-Origin Resource Sharing (CORS) by Jonas Jared Jacek is licensed under CC BY-SA 4.0.
This license requires that reusers give credit to the creator. It allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, for noncommercial purposes only. To give credit, provide a link back to the original source, the author, and the license e.g. like this:
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><a property="dct:title" rel="cc:attributionURL" href="https://www.ditig.com/understanding-cross-origin-resource-sharing-cors">Understanding Cross-Origin Resource Sharing (CORS)</a> by <a rel="cc:attributionURL dct:creator" property="cc:attributionName" href="https://www.j15k.com/">Jonas Jared Jacek</a> is licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="license noopener noreferrer">CC BY-SA 4.0</a>.</p>For more information see the Ditig legal page.