If you're building modern web applications, you've probably seen this message at least once:
Access to fetch at 'https://api.yoursite.com' from origin 'https://yoursite.com' has been blocked by CORS policy.
CORS errors are frustrating because everything may look correct in your code, yet the browser still blocks the request. The important thing to understand is this: CORS is not a bug. It is a security mechanism.
In this guide, we’ll break down why CORS happens and how to fix it properly in Node.js backends, Next.js applications, and Nginx reverse proxy setups.
What is CORS and Why Does It Exist?
CORS stands for Cross-Origin Resource Sharing. Browsers block requests when a frontend application tries to access resources from a different origin without permission.
An origin consists of three parts:
- Protocol (http or https)
- Domain
- Port
If any of these differ between frontend and backend, the browser treats it as cross-origin.
For example:
- Frontend: https://example.com
- Backend: https://api.example.com
This is considered cross-origin.
Understanding Preflight Requests
Before sending certain requests (like POST, PUT, DELETE), the browser sends an OPTIONS request first. This is called a preflight request.
If your server does not respond correctly to this OPTIONS request with proper headers, the actual request never executes.
Fixing CORS in Node.js (Express Backend)
The simplest and safest way to handle CORS in Node.js is by using the cors package.
npm install cors
Then configure it properly:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://yourfrontend.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}));
app.listen(5000, () => {
console.log('Server running on port 5000');
});
Avoid using origin: * in production when credentials are involved.
Manual Header Configuration (Without Package)
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "https://yourfrontend.com");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.header("Access-Control-Allow-Credentials", "true");
if (req.method === "OPTIONS") {
return res.sendStatus(200);
}
next();
});
Fixing CORS in Next.js API Routes
If you're using Next.js API routes, you must explicitly enable CORS.
export default function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', 'https://yourfrontend.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
res.status(200).json({ message: 'Success' });
}
Remember, CORS must be handled at the server level. Frontend cannot override it.
Fixing CORS in Nginx Reverse Proxy
If you're using Nginx in front of your Node app, sometimes CORS headers need to be added at the proxy level.
location / {
proxy_pass http://localhost:5000;
add_header Access-Control-Allow-Origin "https://yourfrontend.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
if ($request_method = OPTIONS) {
return 204;
}
}
After editing configuration:
sudo nginx -t
sudo systemctl restart nginx
Common CORS Mistakes Developers Make
- Allowing wildcard origin with credentials enabled
- Forgetting to handle OPTIONS method
- Setting CORS only in frontend
- Wrong protocol mismatch (http vs https)
- Different port numbers in production
How to Debug CORS Properly
- Check browser network tab
- Inspect preflight OPTIONS response
- Verify response headers
- Check Nginx config if using reverse proxy
- Confirm frontend URL exactly matches allowed origin
Even a trailing slash difference can cause issues.
Production Best Practices
- Whitelist only required domains
- Avoid wildcard in production
- Use environment variables for origin values
- Secure APIs with authentication, not just CORS
CORS is not security by itself. It only controls browser behavior. Always implement proper authentication and authorization.
Final Thoughts
CORS errors look scary, but once you understand the mechanism, they become predictable and manageable.
Most issues happen because headers are incomplete or mismatched between environments. Focus on consistent origin configuration across Node.js, Next.js, and Nginx, and you’ll eliminate most CORS-related problems permanently.
