Tutorial

How to enable CORS in Node.js and Express

Your Express API returns data fine from curl, but the browser blocks it with "No Access-Control-Allow-Origin header is present". The fixes below run from the fastest one-liner to a production-ready setup.

· ~6 min read · Node.js · Express · Updated Oct 13, 2026

The one-line fix (development only)

Install the cors middleware and use it with default settings. It adds Access-Control-Allow-Origin: * to every response:

npm install cors
import express from 'express';
import cors from 'cors';

const app = express();
app.use(cors());  // Allows any origin: DEV ONLY
app.get('/api/users', (req, res) => res.json([{ id: 1, name: 'A' }]));
app.listen(3000);

Three caveats before you ship this to production:

The right way: origin allowlist

For production, reflect a specific origin from a known allowlist. The cors middleware accepts a function:

const ALLOWED_ORIGINS = new Set([
  'https://app.example.com',
  'https://admin.example.com',
  'http://localhost:3000'  // dev only
]);

app.use(cors({
  origin: (origin, callback) => {
    // Same-origin / curl / mobile requests have no Origin header.
    if (!origin) return callback(null, true);
    if (ALLOWED_ORIGINS.has(origin)) return callback(null, origin);
    return callback(new Error(`Origin ${origin} not allowed`));
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
  exposedHeaders: ['X-RateLimit-Remaining'],
  maxAge: 600,            // browser caches preflight for 10 min
  credentials: true       // only needed if the client sends cookies
}));

What each option does:

Without the cors package

If you don't want a dependency, the same behavior is ~10 lines:

const ALLOWED = new Set([
  'https://app.example.com',
  'http://localhost:3000'
]);

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (origin && ALLOWED.has(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Vary', 'Origin');
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.setHeader('Access-Control-Max-Age', '600');
  }
  // Short-circuit OPTIONS preflight before route handlers see it.
  if (req.method === 'OPTIONS') return res.sendStatus(204);
  next();
});

Don't forget Vary: Origin when you echo the request's origin. Without it, a CDN or browser cache can serve a response carrying one origin's Access-Control-Allow-Origin to a different origin, and you get intermittent CORS errors that are hard to reproduce. The cors package adds it for you when it reflects an origin; hand-written middleware has to set it.

Per-route configuration

Many APIs want different rules per route. A public health endpoint can be *, but the auth endpoints need a strict allowlist:

const publicCors  = cors();
const strictCors  = cors({
  origin: ['https://app.example.com'],
  credentials: true
});

app.get('/health', publicCors, (req, res) => res.send('ok'));
app.use('/api/auth', strictCors);
app.use('/api/data', strictCors);

Common gotchas

When you don't control the upstream

If the API you're hitting isn't yours and won't add CORS headers, run it through a CORS proxy. The proxy sends the headers the browser needs. We run a managed one at api.corsproxy.dev (the free tier is 500 requests/day), and publish a separate, simpler open-source relay you can self-host.

Skip the setup

Need to proxy a third-party API instead of configuring CORS yourself? Get a free key.