Skip to content

How to Enable CORS in Express.js

DodaTech 2 min read

In this tutorial, you'll learn about How to Enable CORS in Express.js. We cover key concepts, practical examples, and best practices.

The Problem

Your frontend (React, Vue, or vanilla JS) makes a fetch request to your Express.js backend and gets:

Access to fetch at 'http://localhost:4000/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Browsers block cross-origin HTTP requests by default. Express.js needs to include CORS headers in its responses to tell the browser the cross-origin request is allowed. This security mechanism prevents malicious websites from reading sensitive data from other origins, but it also blocks legitimate API calls during development.

Quick Fix

1. Install and use the cors middleware

npm install cors
const express = require("express");
const cors = require("cors");

const app = express();
app.use(cors());  // Allow all origins (development only)

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

app.listen(4000);

The cors() middleware adds Access-Control-Allow-Origin: * to every response.

2. Restrict to specific origins (production)

const corsOptions = {
  origin: "https://myapp.com",
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
};

app.use(cors(corsOptions));

For production, specify exact origins instead of *. Multiple origins:

const corsOptions = {
  origin: ["https://myapp.com", "https://admin.myapp.com"],
};

3. Handle preflight (OPTIONS) requests

app.options("/api/data", cors());  // Enable preflight for specific route
// Or globally:
app.use(cors());

For non-simple requests (PUT, DELETE, requests with custom headers), browsers send a preflight OPTIONS request first. The cors middleware handles this automatically when applied.

4. Allow credentials (cookies, auth headers)

const corsOptions = {
  origin: "https://myapp.com",
  credentials: true,
};

app.use(cors(corsOptions));

When credentials: true is set, the origin must be specific (not *). The response includes Access-Control-Allow-Credentials: true.

5. Debug CORS with curl

curl -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: GET" \
  -X OPTIONS \
  -v http://localhost:4000/api/data

Expected output:

< access-control-allow-origin: *
< access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
< access-control-allow-credentials: true

If the CORS headers are missing, check that cors() is applied before your route handlers.

6. Dynamic origin (allow-list pattern)

const allowedOrigins = ["https://myapp.com", "https://staging.myapp.com"];

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error("Not allowed by CORS"));
    }
  },
};

The callback-based origin function supports dynamic allow-lists. The !origin check allows server-to-server requests (which have no Origin header).

7. CORS for Express middleware ordering

// CORS must be before your routes
app.use(cors());
app.use(express.json());
app.use("/api", apiRoutes);

CORS middleware must be applied before your route handlers so the response headers are set before the route runs.

Prevention

  • Use app.use(cors()) in development, restrict origins in production
  • Always apply CORS middleware before routes
  • Test preflight with curl -X OPTIONS -v
  • Use environment variables for allowed origins: origin: process.env.CORS_ORIGIN
  • Never use credentials: true with origin: '*' — the browser will reject it
  • Add CORS error handling as Express error middleware to surface configuration problems clearly
  • Remember: CORS is a browser-enforced policy — tools like curl and Postman bypass it

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro