
CORS Isn't a Bug - It's Your API Trying to Warn You (And You Ignored It)
Stop fighting CORS. Understand preflight requests, credentials, wildcard mistakes. CORS isn't a bug—it's your API warning you about real security issues.
Most developers' first reaction to CORS:
"Why is this blocking my request? It works in Postman!"
So you do what everyone does:
app.use(cors({ origin: "*" }));…and move on.
Until one day, something breaks in production. Or worse — something should have been blocked… but wasn't.should have been blocked… but wasn't.
The First Time CORS Slaps You

A very real scenario:
- Frontend: https://app.example.comhttps://app.example.com
- API: https://api.example.comhttps://api.example.com
Everything works in Postman. But in browser:
Access to fetch at 'https://api.example.com' from origin 'https://app.example.com'
has been blocked by CORS policy👉 You think: "API is broken"
👉 Reality: Browser is protecting the userBrowser is protecting the user
What CORS Actually Is (Without the Textbook Definition)
CORS is not a server feature.
👉 It's a browser security rulebrowser security rule
Server just says:
"I allow / don't allow this request"
Browser decides:
"Okay, I trust this… or I block it"
What's Really Happening Behind the Scenes
When your frontend calls API:
- Browser adds:
Origin: https://app.example.com- Server must respond with:
Access-Control-Allow-Origin: https://app.example.com- If not?
👉 Browser blocks it
👉 Even if API returned 200
The Part Everyone Ignores: Preflight Requests
Before some requests, browser sends:
OPTIONS /api/dataThis is called:
👉 Preflight requestPreflight request
When does this happen?
- POST, PUT, DELETE
- Custom headers (AuthorizationAuthorization))
- JSON content-type
Flow:
- Browser: "Can I send this request?"
- Server: "Yes / No"
- Then actual request is sent
👉 If your server doesn't handle OPTIONS → request never reaches your API logicOPTIONS → request never reaches your API logic
Real Mistakes Developers Make
1. Using * with credentials
app.use(cors({
origin: "*",
credentials: true
}));👉 This DOES NOT work
Browser blocks it.
2. Ignoring credentials completely
If you use cookies:
fetch(url, {
credentials: "include"
});Then server MUST return:
Access-Control-Allow-Credentials: true3. Not handling OPTIONS
app.options("*", cors());Without this:
- Preflight fails silently
- Dev thinks API is broken
4. Hardcoding localhost
origin: "http://localhost:3000"👉 Works in dev 👉 Fails in production
5. Fighting CORS instead of understanding it
- Disabling browser security
- Using proxies blindly
- Copy-pasting configs
Real-World Failure (Seen Too Often)
- Dev tests API in Postman → works
- Deploys frontend → fails
- Adds * → partially works* → partially works
- Adds cookies → breaks again
👉 Why?
Because:
Postman doesn't enforce CORS. Browser does.
What Proper CORS Setup Looks Like
Backend (Node.js / Express)
const allowedOrigins = [
"https://app.example.com",
"https://admin.example.com"
];
app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true
}));Headers You Must Understand
Access-Control-Allow-Origin (Who can access) | Access-Control-Allow-Methods (Allowed methods)
Access-Control-Allow-Headers (Allowed headers) | Access-Control-Allow-Credentials (Allow cookies)
How This Connects to Auth (JWT + Cookies)
This is where things get real:
If you use:
- Cookies (HttpOnlyHttpOnly))
- JWT + refresh tokens
Then:
- 👉 CORS MUST allow credentials
- 👉 Origin cannot be **
Otherwise:
- 👉 Auth silently fails
- 👉 Cookies not sent
- 👉 Sessions break
The Mental Model That Changes Everything
Stop thinking:
"CORS is blocking my request"
Start thinking:
"Browser is asking: should this request be trusted?"
Practical Checklist (Do This Today)
- ✅ Define allowed origins (no * in prod)* in prod)
- ✅ Enable credentials if using cookies
- ✅ Handle OPTIONS requestsOPTIONS requests
- ✅ Match frontend + backend domains
- ✅ Test in browser (not just Postman)
Final Thought
CORS errors are not bugs.
They are warnings.
"Something about this request is not safe"
Most developers ignore that warning. Good developers understand it.
Enjoying this article?
Get new articles, tips, and fixes delivered straight to your inbox — free, no spam.
Was this article helpful?
Let me know if this was useful — it helps me write more content like this.
Related Articles
You might also enjoy these
Session Hijacking Starts With Your Cookies — Here's What You Missed
Most developers think session hijacking is an advanced attack. It's not. It usually starts with something very basic: your cookies. Learn the 3 flags and token refresh pattern that actually works.
ESM vs CJS — Why Your import Still Breaks in 2026 and How to Finally Fix It
ERR_REQUIRE_ESM. Missing extensions. No __dirname. The ESM/CJS war is still breaking Node.js projects in 2026. Here's the root cause, all five errors you'll hit, and the exact fixes for each one.


Comments
Leave a Comment
All comments are reviewed before publishing