CORS: Cross-Origin Resource Sharing Explained and Secured
Table of Contents
- Introduction
- What CORS Actually Controls
- Origin vs Site
- Simple Requests and Preflight
- Credentials and Cookies
- Common Secure Configuration
- Common Mistakes
- Checklist
- FAQ
- Conclusion
Introduction
CORS is one of those browser security topics that developers often meet through an error message. The frontend calls an API, the browser blocks the response, and suddenly everyone is changing headers until the request works.
That approach is risky. CORS is not just a random browser annoyance. It controls which origins are allowed to read responses from your API in a browser context.
To configure CORS safely, you need to understand origins, preflight requests, credentials, and why * is not always acceptable.
What CORS Actually Controls
CORS stands for Cross-Origin Resource Sharing.
It tells the browser whether JavaScript running on one origin can read a response from another origin.
Example:
| Frontend origin | API origin | Cross-origin? |
|---|---|---|
https://app.example.com | https://api.example.com | yes |
https://app.example.com | https://app.example.com | no |
http://localhost:3000 | http://localhost:8080 | yes |
CORS is enforced by browsers. It is not a replacement for authentication, authorization, CSRF protection, or server-side validation.
Origin vs Site
An origin includes scheme, host, and port.
https://app.example.com:443
If any part differs, the origin is different.
| URL A | URL B | Same origin? |
|---|---|---|
https://app.example.com | https://app.example.com | yes |
https://app.example.com | https://api.example.com | no |
http://app.example.com | https://app.example.com | no |
http://localhost:3000 | http://localhost:5173 | no |
This is why local development often triggers CORS issues.
Simple Requests and Preflight
Some browser requests are simple. Others require a preflight request.
A preflight is an OPTIONS request the browser sends before the real request. It asks the server whether the actual request is allowed.
Example preflight:
OPTIONS /api/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type
Example response:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: authorization, content-type
If the preflight fails, the browser does not send the real request.
Credentials and Cookies
Credentials change the rules. If a request includes cookies or HTTP auth, the server must explicitly allow credentials.
Access-Control-Allow-Credentials: true
When credentials are allowed, this is not valid:
Access-Control-Allow-Origin: *
The server must return a specific allowed origin.
| Scenario | Safe origin header |
|---|---|
| public read-only API | * can be acceptable |
| cookie-based app API | exact trusted origin |
| multi-tenant dashboard | validated tenant origin |
Common Secure Configuration
A safer CORS setup usually looks like this:
- keep an allowlist of trusted origins
- return the exact origin only if it is allowed
- allow only needed methods
- allow only needed headers
- enable credentials only when required
- keep CORS separate from authentication
Example policy:
| Setting | Example |
|---|---|
| allowed origins | https://app.example.com |
| methods | GET, POST, PATCH, DELETE |
| headers | authorization, content-type |
| credentials | only if cookies are used |
Common Mistakes
Using * with private APIs
For private APIs, a wide-open origin policy can expose responses to untrusted browser contexts.
Reflecting any origin
Some servers return whatever Origin header they receive. That is effectively allow-all behavior.
Confusing CORS with auth
CORS controls browser access to responses. Your API still needs authentication and authorization.
Forgetting preflight
Custom headers, JSON POST, and authorization headers often trigger preflight. Your API must handle OPTIONS correctly.
Allowing credentials without care
Credentialed requests need stricter origin checks because cookies may be sent automatically.
Checklist
- Define the exact origins allowed to call your API.
- Avoid
*for private or credentialed APIs. - Do not reflect arbitrary origins.
- Allow only required methods.
- Allow only required headers.
- Handle
OPTIONSpreflight correctly. - Enable credentials only when needed.
- Keep auth and authorization checks on the server.
- Test local, staging, and production origins separately.
- Log rejected origins during rollout.
FAQ
Does CORS protect my API from non-browser clients?
No. CORS is enforced by browsers. Servers, scripts, curl, and mobile apps are not blocked by CORS.
Why does Postman work but the browser fails?
Postman is not a browser and does not enforce CORS. The browser blocks cross-origin response access based on CORS headers.
Can I disable CORS?
You can loosen CORS, but you should not blindly disable it for private APIs. Configure a precise allowlist instead.
Conclusion
CORS is a browser security boundary, not a random deployment hurdle. It decides which origins can read API responses from browser JavaScript.
Use specific origin allowlists, handle preflight correctly, be careful with credentials, and remember that CORS complements authentication instead of replacing it.
Related Articles
Keep reading within the same topic.
Visual Regression Testing: Catch UI Changes Automatically
Learn visual regression testing with baselines, screenshot diffs, Playwright workflow, snapshot reviews, visual testing tools, and catching UI bugs before users see them.
Fetch API Advanced: Abort, Retry, Timeout, and Middleware
Build resilient Fetch API clients with AbortController, retry logic, timeout handling, middleware patterns, and safer request workflows.
Tailwind CSS Advanced: Performance, Config, and Utilities
Optimize Tailwind CSS with advanced utilities, custom config, responsive patterns, reusable components, build size, and performance tips.
JavaScript Memory Leaks: Detect and Fix with Chrome DevTools
Find and fix JavaScript memory leaks with Chrome DevTools, heap snapshots, listener cleanup, timers, closures, and prevention patterns.