If you’ve built a frontend that talks to an API, chances are you’ve seen this error in your console:
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
has been blocked by CORS policy.
At first glance, it feels random. Your API is up, your code looks fine… so why does the browser stop you?
The answer is CORS (Cross-Origin Resource Sharing) — one of those things that sounds complicated, but once you “get it,” debugging becomes way easier.
Let’s break it down step by step.
What is CORS, really?
CORS is a security feature in browsers. It basically says:
“A website should not be able to make requests to another site unless the other site explicitly says it’s okay.”
Example:
- Your frontend runs at
http://localhost:3000
- Your backend API is at
https://api.example.com
These are different origins (different host/port/protocol), so the browser treats the request as cross-origin.
And here’s the key: without CORS, any malicious website could secretly call APIs on your behalf (like your bank), and steal data.
So when your browser blocks that request, it’s not being mean — it’s protecting you.
How does CORS actually work?
Here’s the flow whenever you hit an API from another origin:
- The Browser Adds an Origin Header Example:
Origin: http://localhost:3000
- The Server Decides if It’s Allowed It must respond with something like:
Access-Control-Allow-Origin: http://localhost:3000
If it matches, the browser lets the request go through. If not → CORS error.
-
Preflight Requests (the “double request” thing you see)
For some requests (
PUT
,DELETE
, custom headers, etc.), the browser first sends an OPTIONS request (aka preflight). The server must reply with allowed methods and headers:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
If anything’s missing, your actual request never gets sent.
Why your request failed (common reasons)
No CORS headers at all
→ The server just didn’t set them.Wrong origin allowed
→ You’re onhttp://localhost:3000
, but the server only allowshttps://myapp.com
.Preflight not handled
→ The server ignores the OPTIONS request, so the browser stops right there.Credentials issue
→ You’re sending cookies/tokens, but the server didn’t set
Access-Control-Allow-Credentials: true
How to fix CORS issues
1. Set CORS headers on the server
In Node.js + Express:
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
(Pro tip: just use the cors
package, it saves you headaches.)
2. Use *
in dev (but not in prod)
Access-Control-Allow-Origin: *
Great for local testing. Terrible for production. Never expose private APIs this way.
3. Enable credentials if needed
res.header("Access-Control-Allow-Credentials", "true");
And in frontend:
fetch(url, { credentials: "include" });
4. Use a proxy during development
If you can’t change the backend (e.g., third-party APIs), use a proxy (Vite/CRA/Webpack dev server) so the request looks like it comes from the same origin.
Best Practices
- Whitelist only the domains you trust.
- Always handle OPTIONS requests properly.
- Remember: CORS exists to protect users, not annoy developers (even though it feels that way sometimes).
Wrapping up
CORS errors are annoying, but they’re also a sign your browser is doing its job.
The trick is to remember:
- The server decides who can call it.
- The browser enforces that rule.
So next time you see the dreaded “blocked by CORS policy”, don’t panic. Check the headers, check the origin, and you’ll know exactly what’s wrong.
Top comments (0)