Fix Express CORS Error: No 'Access-Control-Allow-Origin' Header
Express CORS errors are blocked by the browser, not caused by a server crash. Here's why they happen, how to configure the cors package correctly for production, handle preflight requests, send credentials, and debug CORS in 60 seconds.
What's Actually Happening
The browser blocked the response, not the server. Your Express API received the request and responded fine. The browser checked the response headers, did not find Access-Control-Allow-Origin, and blocked your JavaScript from reading the response.
Two things follow from this:
curland Postman do not enforce CORS. If your API works there but fails in the browser, CORS is the problem.- Adding the right response headers to Express is the entire fix.
The Quickest Fix
cors() with no arguments allows every origin. It unblocks you in development but do not ship this to production. Any website can hit your API.
Production Setup
Lock it to your actual frontend URL:
Set ALLOWED_ORIGIN in your production environment variables. No code changes needed when you deploy to a new domain.
Warning:
origin: '*'andcredentials: truetogether are rejected by browsers. If you are sending cookies or auth headers, you must specify the exact origin.
Multiple Origins
Preflight Requests
Browsers send a preflight OPTIONS request before any POST, PUT, DELETE, or request with custom headers like Authorization. If Express does not respond to OPTIONS correctly, the real request never fires.
The cors package handles this automatically, but only if it runs before your routes:
If you are still seeing preflight failures, add an explicit handler:
Credentials and Auth Headers
If your frontend sends cookies or an Authorization header:
Your Express server needs to match:
Note: The error
Access-Control-Allow-Origin must not be '*' when credentials mode is 'include'means you haveorigin: '*'andcredentials: truetogether. Replace'*'with your exact frontend URL.
Without the cors Package
Diagnosing the Exact Cause
Look for Access-Control-Allow-Origin in the response. If it is missing, cors() is not running on that route. If it is * and you are sending credentials, that is the mismatch.
Quick Reference
| Symptom | Cause | Fix |
|---|---|---|
| Works in Postman, fails in browser | CORS headers missing | Add cors() middleware |
| Works for GET, fails for POST | Preflight not handled | Move cors() before routes or add app.options('*', cors()) |
credentials: true still blocked | Using origin: '*' | Switch to exact origin |
Authorization header blocked | Not in allowedHeaders | Add Authorization to allowedHeaders |
| Works locally, fails in production | Hardcoded localhost origin | Use ALLOWED_ORIGIN env var |
For CORS issues behind an nginx proxy, across multiple API subdomains, or when auth middleware runs before your CORS config, paste your Express setup into DebugAI. It traces the middleware chain and shows exactly where the headers are getting dropped.
Debug faster starting today.
Free VS Code extension. 10 sessions/day. No credit card.