CORS errors are the bane of every frontend developer's existence. Let's demystify how it actually works.
What is the Same-Origin Policy?
Browsers block requests from one origin (protocol + domain + port) to another by default. This prevents malicious sites from reading data from your bank's API.
https://app.com → https://api.com = cross-origin (blocked)https://app.com → https://app.com/api = same-origin (allowed)
How CORS Works
Simple Requests
GET, HEAD, or POST with standard headers go directly. The server responds with Access-Control-Allow-Origin.
Preflight Requests
For "complex" requests (PUT, DELETE, custom headers, JSON content-type), the browser sends an OPTIONS request first:
Common CORS Headers
| Header | Purpose |
|---|---|
| Access-Control-Allow-Origin | Which origins can access (* or specific) |
| Access-Control-Allow-Methods | Allowed HTTP methods |
| Access-Control-Allow-Headers | Allowed custom headers |
| Access-Control-Allow-Credentials | Allow cookies (can't use with *) |
| Access-Control-Expose-Headers | Headers the browser can read |
| Access-Control-Max-Age | How long to cache preflight |
The Credentials Gotcha
If you need cookies or auth headers cross-origin, you must set credentials: "include" on the client AND Access-Control-Allow-Credentials: true on the server. And you CANNOT use * for Allow-Origin — it must be a specific origin.
Debugging Tips
- CORS errors are enforced by the BROWSER, not the server. cURL/Postman won't show CORS errors.
- Check the Network tab for the preflight OPTIONS request
- The actual error message in the console tells you exactly which header is missing