How to Handle CORS Errors in Node.js and Express: A Practical Guide

How to Handle CORS Errors in Node.js and Express: A Practical Guide

by | Aug 4, 2026 | Uncategorized | 0 comments

If you’ve ever built a Node.js API and watched your frontend explode with a red “Access-Control-Allow-Origin” error in the browser console, you already know how frustrating CORS can be. The good news: fixing CORS errors in Node.js is straightforward once you understand what the browser is actually asking for.

In this guide, we walk through what CORS is, why Express keeps throwing these errors, and give you copy-paste ready fixes for every common scenario: credentials, multiple origins, dynamic origins, and preflight requests.

What Is CORS and Why Does Node.js Throw These Errors?

CORS (Cross-Origin Resource Sharing) is a browser security mechanism. When your frontend at https://app.example.com tries to call your API at https://api.example.com, the browser blocks the request unless the server explicitly says “yes, this origin is allowed” via HTTP headers.

Node.js and Express do not add these headers by default. That’s why the moment your frontend and backend run on different origins (different domain, subdomain, or even port), the browser refuses the response.

An Origin = Protocol + Domain + Port

Frontend Backend Same Origin?
http://localhost:3000 http://localhost:5000 No (different port)
https://app.site.com https://api.site.com No (different subdomain)
https://site.com http://site.com No (different protocol)
nodejs code error

Common CORS Error Messages (And What They Really Mean)

Here are the exact console errors developers see, translated into plain English:

  • “Access to XMLHttpRequest at ‘…’ from origin ‘…’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
    Your server didn’t send the CORS header at all. Fix: enable the cors middleware.
  • “Response to preflight request doesn’t pass access control check”
    The browser sent an OPTIONS request first and your server didn’t respond correctly.
  • “The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’.”
    You’re sending cookies but using origin: '*'. You must specify an exact origin.
  • “Method PATCH is not allowed by Access-Control-Allow-Methods”
    You need to explicitly allow that HTTP method.
  • “Request header field authorization is not allowed by Access-Control-Allow-Headers”
    Your custom header isn’t whitelisted.

How to Fix CORS Errors in Node.js: Step-by-Step

Step 1: Install the cors Middleware

The official Express cors package handles 95% of use cases. Install it:

npm install cors

Step 2: The Quick Fix (Development Only)

If you just want to make it work locally, allow all origins:

const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors()); // Allows all origins

app.get('/api/data', (req, res) => {
  res.json({ message: 'CORS is working' });
});

app.listen(5000);

Warning: Never ship app.use(cors()) to production without restrictions. It opens your API to any website on the internet.

Step 3: Production Setup with a Specific Origin

app.use(cors({
  origin: 'https://app.yourdomain.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));
nodejs code error

Scenario 1: Allowing Multiple Origins

You probably need to allow localhost during development and your production domain at the same time. Use an array or a function:

const allowedOrigins = [
  'http://localhost:3000',
  'http://localhost:5173',
  'https://app.yourdomain.com',
  'https://staging.yourdomain.com'
];

app.use(cors({
  origin: function (origin, callback) {
    // Allow requests with no origin (mobile apps, curl, Postman)
    if (!origin) return callback(null, true);

    if (allowedOrigins.includes(origin)) {
      return callback(null, true);
    }
    return callback(new Error('Not allowed by CORS'));
  }
}));

Scenario 2: Sending Cookies or Authorization Headers (Credentials)

If your frontend sends cookies or auth tokens, you need credentials: true and you cannot use the * wildcard.

Backend (Express)

app.use(cors({
  origin: 'https://app.yourdomain.com',
  credentials: true
}));

Frontend (fetch)

fetch('https://api.yourdomain.com/user', {
  method: 'GET',
  credentials: 'include'
});

Frontend (axios)

axios.get('https://api.yourdomain.com/user', {
  withCredentials: true
});

Scenario 3: Fixing Preflight (OPTIONS) Requests

Browsers send a preflight OPTIONS request before any “non-simple” request (PUT, DELETE, PATCH, or requests with custom headers like Authorization).

The cors package handles this automatically, but if you have custom middleware or authentication running before it, the preflight can be rejected. The fix is to make sure CORS runs before your auth middleware:

// Correct order
app.use(cors(corsOptions));
app.use(express.json());
app.use(authMiddleware); // auth AFTER cors

app.use('/api', routes);

You can also explicitly enable preflight for all routes:

app.options('*', cors(corsOptions));
nodejs code error

Scenario 4: Per-Route CORS Configuration

Sometimes you want a public endpoint (open to everyone) and private endpoints (restricted). Apply CORS per route:

// Public endpoint - open to all
app.get('/api/public', cors(), (req, res) => {
  res.json({ status: 'ok' });
});

// Private endpoint - restricted
const privateCors = cors({ origin: 'https://app.yourdomain.com' });
app.get('/api/private', privateCors, (req, res) => {
  res.json({ secret: 'data' });
});

Fixing CORS Without the cors Package (Manual Headers)

If you don’t want an extra dependency, you can set headers manually:

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://app.yourdomain.com');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    return res.sendStatus(204);
  }
  next();
});

Still, we recommend the cors package because it correctly handles edge cases like the Vary header, which is critical when caching responses for multiple origins.

Common Mistakes That Keep Breaking CORS

  1. Putting CORS after authentication middleware. Preflight requests don’t include credentials, so your auth will reject them.
  2. Using origin: '*' with credentials: true. The browser will reject this combination.
  3. Forgetting the protocol. yourdomain.com is not a valid origin. It must be https://yourdomain.com.
  4. Trailing slashes. https://site.com/ and https://site.com are treated differently by some checks.
  5. Assuming CORS is a server error. It’s a browser policy. Postman, curl, and server-to-server calls ignore CORS entirely.
  6. Deploying behind a reverse proxy (Nginx, Cloudflare) that strips headers. Check your proxy config too.
nodejs code error

Debugging Checklist

  • Open DevTools > Network tab and click the failing request.
  • Check the Response Headers for Access-Control-Allow-Origin.
  • Look for a preceding OPTIONS request and inspect its status. It should return 204 or 200.
  • Confirm the Origin header sent by the browser matches what you allow on the server.
  • Test the endpoint with curl. If it works with curl but fails in the browser, it’s 100% a CORS config issue.

Complete Production-Ready Example

const express = require('express');
const cors = require('cors');

const app = express();

const allowedOrigins = [
  'https://app.yourdomain.com',
  'https://admin.yourdomain.com',
  process.env.NODE_ENV === 'development' && 'http://localhost:3000'
].filter(Boolean);

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Blocked by CORS policy'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
  maxAge: 86400 // Cache preflight for 24h
};

app.use(cors(corsOptions));
app.options('*', cors(corsOptions));
app.use(express.json());

app.get('/api/health', (req, res) => res.json({ status: 'ok' }));

app.listen(5000, () => console.log('API running on port 5000'));

FAQ

Why does CORS only fail in the browser and not in Postman?

CORS is enforced by the browser, not the server. Postman, curl, and server-to-server requests don’t run the CORS check, so they get the response normally. The server always sends the data; the browser just refuses to hand it to your JavaScript.

Is CORS a security feature I can just disable?

You can’t disable it from the frontend. CORS protects your users’ browsers from malicious cross-origin requests. The correct approach is to configure your server to explicitly allow the origins that should have access.

Can I fix CORS from the frontend?

No. CORS headers must be set by the server. Frontend workarounds (like disabling browser security or using a proxy) are only useful for local development.

What is a preflight request?

Before certain requests (like PUT, DELETE, or requests with custom headers), the browser sends an OPTIONS request to check if the actual request is allowed. Your server must respond to this with the appropriate CORS headers and a 200 or 204 status.

Should I use origin: '*' in production?

Only for truly public APIs that don’t require authentication or cookies. For any authenticated endpoint, always specify exact origins.

Does the order of middleware matter?

Yes, absolutely. Always register cors() before authentication, body parsers that reject requests, or route handlers. Otherwise, preflight requests may fail before CORS headers are added.

Wrapping Up

CORS errors in Node.js feel scary, but they follow predictable rules. Install the cors middleware, configure your allowed origins explicitly, handle credentials correctly, and make sure CORS runs early in your middleware chain. With the snippets above, you should be able to fix any CORS error your Express app throws at you.

Need help architecting a secure Node.js API or dealing with more complex authentication scenarios? The team at Box Software builds and audits production-grade Node.js backends every day. Get in touch through our contact page.