You’re building a frontend that calls your API, and suddenly:
Your API works fine in Postman. It works with curl. But your browser refuses to load the data. What’s going on?
Why CORS Exists
CORS (Cross-Origin Resource Sharing) is a browser security feature, not a server bug. It prevents malicious websites from making requests to APIs on your behalf.
An “origin” is the combination of protocol, domain, and port:
http://localhost:3000(your React app)http://localhost:8000(your API)
These are different origins because the ports differ. The browser blocks cross-origin requests by default unless the server explicitly allows them.
Key insight: CORS is enforced by browsers, not servers. That’s why curl works—it doesn’t care about CORS.
The Fix: Add CORS Headers
Your server needs to tell the browser “yes, this origin is allowed to access me.”
Node.js / Express
| |
Python / FastAPI
| |
Python / Flask
| |
Nginx (Reverse Proxy)
| |
The Preflight Problem
If you’re sending custom headers (like Authorization) or using methods other than GET/POST, the browser sends a preflight OPTIONS request first.
Your server must handle this:
| |
Symptom: Your GET requests work but POST/PUT fail, or requests with Authorization headers fail. Check if OPTIONS requests are returning proper CORS headers.
Common Mistakes
1. Wildcard with Credentials
This doesn’t work:
| |
If you need credentials: true (for cookies), you must specify exact origins, not *.
2. Missing Headers in Error Responses
CORS headers must be present on all responses, including errors. If your 500 error page doesn’t have CORS headers, the browser shows a CORS error instead of the actual error.
| |
3. Protocol Mismatch
http://localhost:3000 and https://localhost:3000 are different origins. Make sure your allowed origins match exactly what your frontend uses.
Quick Debugging Checklist
Check the actual response headers - Open DevTools → Network → click the failed request → Headers tab. Is
Access-Control-Allow-Originpresent?Check for preflight - Look for an OPTIONS request before your actual request. Is it returning 200/204 with proper headers?
Check your origin exactly - Copy the error message’s origin and make sure it matches your allowed origins list exactly.
Test without credentials first - Remove
credentials: 'include'from your fetch andcredentials: truefrom your server to isolate the issue.
The “Just Make It Work” Development Fix
During local development, if you just need it to work:
| |
Never deploy this to production. It allows any website to call your API.
Production Checklist
- Specify exact allowed origins (no wildcards with credentials)
- Handle OPTIONS preflight requests
- Include CORS headers on error responses
- Test with actual production domains before deploying
CORS errors are frustrating because the browser hides the real response. But once you understand it’s just missing headers, the fix is straightforward.