How to Add CORS Middleware to an Express API

Express APIs work fine in curl and Postman but block browser requests because browsers enforce CORS - they require Access-Control headers the server must send explicitly. Adding a single middleware function before your routes fixes all of it.

Backend Engineernodejsexpresscors

Why browsers block the request (and curl doesn't)

CORS is a browser construct, not an HTTP protocol rule. When a browser sees a fetch to a different origin, it checks the response for an Access-Control-Allow-Origin header before handing the data to your JavaScript. curl and Postman skip that check entirely - they are not browsers.

So the API is not broken. It just has no CORS headers. Browsers refuse the response; curl succeeds. The fix is a middleware that attaches the headers to every response.

The middleware

Register it with app.use() before any route handler so every response gets the headers:

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    return res.status(204).end();
  }

  next();
});

Why OPTIONS needs special handling

Before sending a POST, PUT, or any request with a custom header, the browser sends an automatic preflight using the OPTIONS method. If your route handlers don't define OPTIONS, Express returns 404 and the browser aborts before the real request ever fires. The middleware above intercepts every OPTIONS call and returns 204 with the CORS headers, so the preflight succeeds.

Verify it:

# Preflight - should return 204 with Access-Control headers
curl -v -X OPTIONS http://localhost:3000/tasks

# Real request - headers should appear on the response
curl -sD - http://localhost:3000/tasks | grep -i access-control

When to tighten the wildcard

Access-Control-Allow-Origin: * is fine for public, read-only APIs. Two scenarios require a specific origin instead of *:

Production CORS middleware usually reads req.headers.origin, checks it against an allowlist, and echoes it back if it matches:

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

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (ALLOWED.has(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Vary', 'Origin');    // tell caches this header changes per origin

  if (req.method === 'OPTIONS') return res.status(204).end();
  next();
});

The Vary: Origin header tells CDNs and proxies not to serve one origin's cached response to another.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

Why does my Express API work in curl but not in the browser?

CORS is a browser-only rule. Curl and Postman skip it. The browser checks for Access-Control-Allow-Origin on the response and blocks the data if it's missing. Adding CORS middleware to Express solves it - curl behavior is unchanged.

What is a CORS preflight request?

Before sending a non-simple request (POST with JSON, any request with an Authorization header, etc.), the browser sends an OPTIONS request to ask the server if that request is allowed. Your Express app needs to respond 204 with the Access-Control headers or the real request never fires.

Should I use the cors npm package or write it manually?

Either works. The cors package is convenient and well-tested. Writing it manually - res.setHeader + an OPTIONS check - is about 8 lines and teaches you exactly what headers do what. For production, the cors package handles edge cases like Vary and credential modes more robustly.

What is CORS middleware?

CORS middleware runs on each request to add the Access-Control-Allow-Origin and related headers so browsers allow cross-origin requests. In Express it can be the cors package or a few lines you write yourself.

What is Express middleware?

Express middleware is a function with access to the request, response, and next() that runs in order on every request - used for parsing, auth, logging, CORS, and error handling before the route handler.

Keep learning

Return the Correct HTTP Status CodeBackend projectRepair Broken API PaginationBackend projectAdd a Health Check Endpoint in GoBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →