I was connecting my React frontend to my Express backend and ran into this:
"Access to fetch at 'http://localhost:5000/message' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present."
I panicked. I thought my server was broken. It wasn't.
What CORS actually is
CORS stands for Cross-Origin Resource Sharing. An "origin" is your protocol + domain + port combined. localhost:5173 and localhost:5000 are two different origins — even on the same machine.
Browsers have a built-in rule: JavaScript cannot read responses from a different origin unless that origin explicitly allows it. This exists to prevent malicious websites from making requests to other sites using your credentials without you knowing.
The part that confused me
My Express server was receiving the request. It was sending back a response. The browser just refused to give that response to my JavaScript because the response didn't include an Access-Control-Allow-Origin header.
CORS is entirely browser-enforced. That's why Postman never throws CORS errors — it's not a browser.
The fix
bash
npm install cors
javascriptconst cors = require('cors');
app.use(cors({
origin: 'http://localhost:5173'
}));
That's it. Express now attaches the right header to every response, and the browser releases it to your JavaScript.
What about POST and DELETE?
For anything beyond simple GET requests — or when you add an Authorization header — the browser first sends an OPTIONS "preflight" request asking your server if the operation is allowed. The cors package handles this automatically, so you don't need extra configuration for basic cases.
For production
Never use origin: '*' in production. Lock it to your actual domain:
js
app.use(cors({
origin: process.env.NODE_ENV === 'production'
? 'https://yourdomain.com'
: 'http://localhost:5173'
}));
CORS isn't complicated once you understand the browser is the one enforcing it — not your server.
Top comments (0)