CORS Explained: Origins, Preflight, Credentials, and Safe Configuration
Understand CORS through browser origins, preflight requests, credentials, allow-lists, and a practical debugging checklist.
0xNN · · 6 min read
CORS Explained: Origins, Preflight, Credentials, and Safe Configuration
The browser error usually says “blocked by CORS policy”, which makes it sound like the API rejected the request. Often the API responded perfectly; the browser refused to expose that response to a page from another origin.
What counts as an origin?
An origin is the combination of scheme, host, and port. https://app.example.com and https://api.example.com are different origins even though they share a parent domain. A different port is different too.
CORS is a browser permission mechanism. It is not authentication, a firewall, or a way to stop a server-to-server client from calling your API. A curl request does not enforce the browser's same-origin policy.
The simple request path
For some methods and headers, the browser sends the request and checks the response headers. The server must return an Access-Control-Allow-Origin value that matches the requesting origin, or the browser hides the response from JavaScript.
Do not blindly return * when the response contains private data. A wildcard is also incompatible with credentialed requests.
Why preflight exists
For a non-simple request—such as PATCH, a custom header, or a JSON content type—the browser first sends an OPTIONS request. This is the preflight. It asks whether the target origin, method, and headers are allowed.
The server must answer with the relevant Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers headers. Proxies and routers must let OPTIONS reach the correct handler. Cache layers should vary on the Origin header when responses differ by origin.
Credentials change the rules
Cookies and HTTP authentication are credentials. If the browser sends them, the server must opt in with Access-Control-Allow-Credentials: true and a specific allowed origin. The server also needs CSRF protection appropriate to the cookie setup. CORS does not replace CSRF protection.
Keep the allow-list explicit in production. Deriving “allowed” from a user-controlled Origin header without checking it against a trusted list turns the header into an echo.
A diagnosis checklist
1. Read the browser Network panel, not only the console summary.
2. Check the request origin, method, headers, and whether an OPTIONS request ran.
3. Inspect the response status and CORS headers for both preflight and actual requests.
4. Confirm redirects, CDN rules, and error responses carry the required headers.
5. Test an origin that should be denied.
If the server returns 401 or 500, fix that server error first. Adding a permissive CORS header can hide the real problem while exposing more than intended.
References
• MDN: Cross-Origin Resource Sharing
• Fetch standard: CORS protocol