CORS

Fix CORS Error in Express.js

Your Express server is returning 200. The browser is blocking it anyway. That is CORS — the server responded but did not tell the browser the request was allowed. One middleware call fixes it.

Browser Console Error
Access to fetch at 'https://api.example.com/data' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Install and apply cors middleware

npm install cors
const express = require('express');
const cors = require('cors');
const app = express();

// Development — allows all origins (never use in production)
app.use(cors());

// Production — lock it to your frontend domain
app.use(cors({
  origin: 'https://app.example.com',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

app.get('/api/data', (req, res) => res.json({ ok: true }));
app.listen(3000);

Put app.use(cors()) before your route definitions. Middleware runs in declaration order — routes defined before the cors() call will not get CORS headers.

If you are sending cookies or auth headers

When your frontend sends credentials: 'include', the wildcard origin stops working. The browser spec forbids * with credentials. You need an explicit origin.

// Breaks when frontend uses credentials: 'include'
app.use(cors({ origin: '*', credentials: true })); // ❌

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

If OPTIONS preflight is failing

Browsers send an OPTIONS request before POST, PUT, or DELETE when custom headers are present. Add an explicit OPTIONS handler before the main cors() call.

app.options('*', cors()); // Handle all preflight requests
app.use(cors({ origin: 'https://app.example.com' }));

Multiple allowed origins

const allowed = ['https://app.example.com', 'https://staging.example.com'];

app.use(cors({
  origin: function(origin, callback) {
    if (!origin || allowed.includes(origin)) {
      callback(null, origin);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
}));

// Add Vary: Origin so CDNs cache per-origin correctly
app.use((req, res, next) => { res.vary('Origin'); next(); });

Per-route CORS

If you only need CORS on specific routes, pass the cors middleware directly to each route:

const corsOpts = { origin: 'https://app.example.com' };

app.get('/api/public', cors(corsOpts), (req, res) => res.json({ data: 'ok' }));
app.post('/api/submit', cors(corsOpts), (req, res) => res.json({ status: 'received' }));

Not sure if your CORS config is correct? CORSFixer sends a real OPTIONS preflight to your API and shows exactly what headers are missing or wrong.

Test your CORS config live → CORSFixer