Explain CORS and How to Debug It
Every frontend developer has hit a CORS error. Seniors understand why it exists and how to fix it properly.
What CORS Is
Cross-Origin Resource Sharing is a browser security mechanism. It prevents JavaScript on origin-a.com from reading responses from origin-b.com unless origin-b.com explicitly allows it.
An "origin" is: protocol + hostname + port. Even http://localhost:3000 and http://localhost:4000 are different origins.
How It Works
Simple Requests
For GET/POST with standard headers, the browser sends the request directly with an Origin header:
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.example.comThe server responds with:
Access-Control-Allow-Origin: https://app.example.comIf the header is missing or doesn't match, the browser blocks JavaScript from reading the response.
Preflight Requests
For non-simple requests (PUT/DELETE, custom headers, JSON content-type), the browser sends an OPTIONS request first:
OPTIONS /api/data HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, AuthorizationThe server must respond with:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT, POST, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400Common Mistakes
- Using
*with credentials âAccess-Control-Allow-Origin: *cannot be used withcredentials: 'include' - Missing preflight handling â server must respond to OPTIONS requests
- Proxy confusion â CORS is a browser-only restriction; server-to-server requests skip it entirely
- Caching issues â
Vary: Originis needed when multiple origins are allowed
Debugging Checklist
- Check the Network tab for the preflight OPTIONS request
- Verify the response headers include the correct
Access-Control-Allow-*values - Check if credentials mode requires a specific origin (not
*) - Confirm the server handles OPTIONS method
- Check if a proxy or CDN is stripping CORS headers
CORS isn't a bug â it's a security feature. The fix is always on the server, never a browser hack.