The Nightclub Bouncer Analogy
A nightclub has a bouncer:
- VIP list: These people can enter
- Not on list: "Sorry, you can't come in"
The bouncer checks before letting anyone in.
CORS is a bouncer for your API. It decides which websites can access your server's data.
The Problem CORS Solves
Same-Origin Policy
Browsers enforce "Same-Origin Policy":
Scripts on site A can only access data from site A
Scripts on site A CANNOT access data from site B
Origin = protocol + domain + port
https://example.com:443
Why This Matters
Without protection:
You visit malicious-site.com
Malicious script: "Fetch user's bank data from bank.com"
Your cookies sent! Bank data stolen!
With Same-Origin Policy:
Browser blocks the request
"Not same origin, can't access"
But Sometimes You NEED Cross-Origin
API at: api.mycompany.com
Frontend at: app.mycompany.com
Different origins! Blocked by default.
Enter CORS: "I'll allow app.mycompany.com"
How CORS Works
The Headers
Request from app.mycompany.com to api.mycompany.com:
Server checks origin, responds with:
Access-Control-Allow-Origin: https://app.mycompany.com
Browser sees header:
"Origin is allowed, let the script access the response"
Simple vs Preflight Requests
Simple Requests
Conditions (all must be true):
- GET, HEAD, or POST
- Standard headers only
- No custom headers
Flow:
Browser → Request → Server
Server → Response with CORS headers
Browser → Allow or block based on headers
Preflight Requests
When request has:
- PUT, DELETE, PATCH methods
- Custom headers
- Non-standard content types
Flow:
Browser → OPTIONS request (preflight)
Server → "Yes, allowed" or "No, blocked"
Browser → Actual request (if allowed)
Preflight Flow
1. Browser: OPTIONS /api/users
Origin: https://app.mycompany.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header
2. Server: 200 OK
Access-Control-Allow-Origin: https://app.mycompany.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: X-Custom-Header
3. Browser: DELETE /api/users
(Actual request)
CORS Headers Explained
Response Headers
| Header | Purpose |
|---|---|
Access-Control-Allow-Origin | Which origins can access |
Access-Control-Allow-Methods | Which HTTP methods allowed |
Access-Control-Allow-Headers | Which headers allowed |
Access-Control-Allow-Credentials | Allow cookies/auth? |
Access-Control-Expose-Headers | Headers script can read |
Access-Control-Max-Age | Cache preflight for X seconds |
Example Configuration
Allow specific origin:
Access-Control-Allow-Origin: https://myapp.com
Allow multiple methods:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Allow credentials (cookies):
Access-Control-Allow-Credentials: true
(Note: Cannot use * with credentials!)
Cache preflight:
Access-Control-Max-Age: <seconds> (cache for a while)
Common Configurations
Allow Single Origin
Access-Control-Allow-Origin: https://app.mycompany.com
Most restrictive. Only this origin can access.
Allow Multiple Origins (Dynamic)
Check request Origin header
If origin is in your allowed list:
Set Access-Control-Allow-Origin to that origin
Else:
Block or omit header
You check server-side, respond dynamically.
Allow All Origins (Dangerous!)
Access-Control-Allow-Origin: *
Anyone can access your API!
Cannot use with credentials.
OK for public data only.
CORS with Credentials
Sending Cookies Cross-Origin
Client must:
fetch(url, { credentials: 'include' })
Server must:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://specific-origin.com
(Cannot be *)
Both sides must opt in!
Common CORS Errors
"No 'Access-Control-Allow-Origin' header"
Cause: Server didn't send CORS headers
Fix: Configure server to send appropriate headers
"Wildcard not allowed with credentials"
Cause: Using * with credentials: true
Fix: Use specific origin, not wildcard
"Preflight response didn't succeed"
Cause: Server returned error for OPTIONS request
Fix: Ensure server handles OPTIONS and returns 200
Security Considerations
Don't Use * with Sensitive Data
Access-Control-Allow-Origin: *
Any website can read your API responses!
Only for truly public APIs.
Validate Origins Server-Side
Don't trust the Origin header blindly.
Maintain an explicit allowlist.
Check against it before responding.
Be Careful with Credentials
Access-Control-Allow-Credentials: true
Now cookies are sent cross-origin.
Make sure you really need this.
Common Mistakes
1. Thinking CORS Is Backend Security
CORS is a BROWSER feature.
Browsers enforce it.
Attackers with curl ignore it completely.
CORS protects users, not your server.
Use authentication for server security.
2. Reflecting Any Origin
Bad: Whatever Origin is sent, allow it
If (origin exists) {
Allow-Origin: origin
}
This is the same as allowing everything!
Validate the origin against a list.
3. Not Handling OPTIONS
DELETE request → Browser sends OPTIONS first
Server returns 404 for OPTIONS → CORS fails
Handle OPTIONS method explicitly.
FAQ
Q: Why doesn't my API work from the browser but works from Postman?
Postman isn't a browser - it doesn't enforce CORS. Browsers do.
Q: Can I disable CORS?
In development, you can use browser extensions or proxy. In production, configure your server properly.
Q: Does CORS protect my API?
No! CORS protects browsers from malicious scripts. Anyone can still call your API directly. Use authentication.
Q: Why is preflight needed?
To check permissions before making changes. DELETE shouldn't happen if not allowed.
Summary
CORS controls which websites can access your API from browsers, protecting users from malicious cross-origin requests.
Key Takeaways:
- Browsers enforce Same-Origin Policy
- CORS allows controlled exceptions
- Simple requests: one request
- Complex requests: preflight first
- Use specific origins, not *
- CORS is browser protection, not API security
CORS is the bouncer that helps protect users in the browser context.
Related Concepts
Leave a Comment
Comments (0)
Be the first to comment on this concept.
Comments are approved automatically.