Cross-Origin Resource Sharing (CORS)
CORS is a browser-enforced security mechanism that restricts how JavaScript running on one origin can read responses from another origin.
Key points:
- CORS is enforced only by browsers
- CORS does not block requests from being sent
- CORS controls response visibility, not server execution
If you see a CORS error, the server was still reached.
Origin vs Same-Origin Policy (SOP)
What Is an Origin?
An origin is defined as:
scheme + host + portExamples:
https://example.com≠http://example.comhttps://example.com≠https://api.example.comhttps://example.com:443≠https://example.com:8443
Same-Origin Policy (SOP)
The Same-Origin Policy states:
JavaScript may freely read responses only from the same origin.
Everything else is blocked by default.
CORS exists as a controlled relaxation of SOP.
What CORS Actually Protects
| Capability | Allowed cross-origin? |
|---|---|
| Sending requests | Yes |
| Submitting forms | Yes |
| Loading images/scripts | Yes |
| Reading response body | No (unless CORS allows it) |
| Reading headers | No (unless exposed) |
Critical insight
CORS protects data confidentiality, not server integrity.
Browser Mental Model
- JavaScript on
frontend.comcallsapi.backend.com - Browser sends the HTTP request
- Server processes the request
- Browser inspects response headers
- Browser either:
- Exposes the response to JavaScript
- Or blocks it with a CORS error
Simple Requests vs Preflighted Requests
Simple Requests (No Preflight)
A request is simple if all are true:
- Method is
GET,POST, orHEAD - Headers are simple (
Accept,Content-Type, etc.) - Content-Type is one of:
application/x-www-form-urlencodedmultipart/form-datatext/plain
Result:
- Request is sent immediately
- No
OPTIONSpreflight
Preflighted Requests
Anything else triggers a preflight, for example:
PUT,PATCH,DELETE- Custom headers (
Authorization,X-*) application/json
Preflight in Detail
Preflight Request
OPTIONS /resourceOrigin: https://frontend.comAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: AuthorizationRequired Preflight Response
Access-Control-Allow-Origin: https://frontend.comAccess-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: AuthorizationIf the response is missing or mismatched:
- Browser blocks JavaScript access
- Actual request may never be sent
Credentials and CORS (Most Common Pitfall)
Credentialed Requests
Credentials include:
- Cookies
- HTTP authentication
- Client TLS certificates
To allow credentials:
Access-Control-Allow-Credentials: trueRules:
Access-Control-Allow-Originmust be explicit*is invalid when credentials are used
Why Wildcard Origins Break
Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true # INVALIDBrowser behavior:
- Always blocked
FastAPI CORS Configuration
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],)Common Failure Modes
| Symptom | Root Cause |
|---|---|
| Works in curl, fails in browser | CORS is browser-only |
| GET works, POST fails | Preflight rejected |
| Cookies not sent | Credentials not enabled |
| Auth header blocked | Header not allowed |
| Intermittent failures | Origin mismatch |
CORS vs CSRF
| Aspect | CORS | CSRF |
|---|---|---|
| Protects | Response data | User intent |
| Threat | Data exfiltration | State change |
| Blocks request | No | No |
| Browser enforced | Yes | Partially |
| Server enforced | No | Yes |
Why APIs Work Outside Browsers
Tools that ignore CORS:
- curl
- Postman
- Mobile apps
- Server-to-server calls
CORS exists only to protect browsers from malicious JavaScript.
Interview Traps
- “CORS prevents CSRF” → false
- “CORS blocks the request” → false
- “Disable CORS in production” → dangerous
- “CORS is a backend security feature” → incorrect
Rules of Thumb
- If JS cannot read the response → CORS
- If backend still sees traffic → expected
- If cookies involved → explicit origin + credentials
- If JSON + auth → expect preflight
- Never combine
*with credentials