CORS: The Developer’s Nightmare: A Scenario-Based Guide to Fixing it Without *
We’ve all been there. You’ve spent hours perfecting your Node.js API and your React frontend. You trigger your first fetch() request, and instead of data, you get a wall of red text in the console:
"Access to fetch at 'https://www.google.com/search?q=api.mysite.com' from origin 'mysite.com' has been blocked by CORS policy..."
In a moment of frustration, many developers reach for the "nuclear option": setting Access-Control-Allow-Origin to *. While this makes the error go away, it’s the security equivalent of leaving your front door wide open because the lock was sticking.
Here is how to navigate the most common CORS nightmares like a pro.
What is CORS actually doing?
Cross-Origin Resource Sharing (CORS) is not a bug; it is a browser security feature. It prevents a malicious website from making requests to your API on behalf of a user.
The browser uses a Preflight Request (an OPTIONS call) to ask the server: "Hey, is this domain allowed to talk to you?" If the server doesn't explicitly say "Yes," the browser kills the request.
Scenario 1: The "I have multiple environments" Problem
The Setup: You have a local development server (localhost:3000), a staging site, and a production site. You need all of them to access your API.
The Wrong Fix:
// Hardcoding one domain breaks the others
res.setHeader('Access-Control-Allow-Origin', 'https://mysite.com');
The Pro Fix:
Create a "Whitelist" and check the incoming Origin header against it.
const whitelist = ['http://localhost:3000', 'https://staging.mysite.com', 'https://mysite.com'];
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
Note: !origin allows tools like Postman or server-to-server requests to still function.
Scenario 2: The "I need to send Cookies" Problem
The Setup: You are using credentials: 'include' in your fetch request because you need to send session cookies or HttpOnly JWTs.
The Nightmare: When you use credentials, the browser strictly forbids the use of *. If you try it, the request will fail.
The Fix: 1. Reflect the Origin: You must echo back the specific origin of the requester.
- Allow Credentials: Set the
Access-Control-Allow-Credentialsheader totrue.
// Example in Express.js
app.use(cors({
origin: 'https://mysite.com',
credentials: true
}));
Scenario 3: The "Custom Headers" Problem
The Setup: You are sending a custom header for tracking or versioning, such as X-API-Version: v1.
The Nightmare: Even if the origin is allowed, the browser will block the request because it considers X-API-Version a "non-standard" header.
The Fix:
You must explicitly tell the browser that this specific header is safe to read.
Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Version
Summary Checklist: Fixing CORS the Right Way
| Action | Why? |
|---|---|
| Check the trailing slash |
https://mysite.com and https://mysite.com/ are different origins. |
| Match the Protocol |
http and https are not the same in the eyes of CORS. |
| Verify Port Numbers |
localhost:3000 is a different origin than localhost:4000. |
| Use a Middleware | Use established libraries like the cors npm package instead of manual headers to avoid typos. |
The Ultimate Rule
CORS is a browser-only concept. If you are still seeing "Access Blocked" after following these steps, check your server-side logs. Sometimes a 500 Internal Server Error on your API causes the CORS headers not to be sent, leading the browser to report a CORS error instead of the actual backend crash.
Top comments (0)