Implementing CORS

1. Installing cors Package

Example: Install

npm i cors

2. Enabling CORS for All Routes

Example: Default (allow any origin)

import cors from "cors";
app.use(cors());  // Access-Control-Allow-Origin: *
Warning: cors() with no options allows any origin and forbids credentials. Lock down with origin: in production.

3. Configuring Allowed Origins

Example: Allowlist

const allowed = new Set(["https://app.example.com","https://admin.example.com"]);
app.use(cors({
  origin: (origin, cb) => {
    if (!origin || allowed.has(origin)) return cb(null, true);
    return cb(new Error("CORS: Origin not allowed"));
  }
}));

4. Setting Allowed Methods

Example: methods

app.use(cors({
  methods: ["GET","POST","PUT","PATCH","DELETE"],
  allowedHeaders: ["Content-Type","Authorization","Idempotency-Key"]
}));

5. Allowing Credentials

Example: Credentials

app.use(cors({
  origin: "https://app.example.com",  // MUST be exact, NOT "*"
  credentials: true
}));
credentialsRequired for
trueCookies, Authorization header, TLS client certs
falsePublic APIs without per-user state

6. Setting Exposed Headers

Example: exposedHeaders

app.use(cors({
  exposedHeaders: ["X-Total-Count","Link","X-Request-Id"]
}));

7. Setting Preflight Max Age

Example: maxAge

app.use(cors({ maxAge: 86400 }));  // cache preflight for 24h

8. Enabling CORS for Specific Routes

Example: Per-route

app.get("/public/data", cors(), (req, res) => res.json({ ok: true }));
app.options("/public/data", cors());  // preflight

9. Handling Dynamic Origins

Example: Subdomain regex

const ORIGIN_RE = /^https:\/\/([a-z0-9-]+\.)?example\.com$/;
app.use(cors({
  origin: (origin, cb) => (!origin || ORIGIN_RE.test(origin)) ? cb(null, true) : cb(new Error("Not allowed")),
  credentials: true
}));

10. Pre-flight Request Handling

Browser                                Server
  │                                       │
  │── OPTIONS /api/users ────────────────▶│
  │   Origin: https://app.example.com     │
  │   Access-Control-Request-Method: PUT  │
  │   Access-Control-Request-Headers: ... │
  │                                       │
  │◀── 204 No Content ────────────────────│
  │   Access-Control-Allow-Origin: ...    │
  │   Access-Control-Allow-Methods: ...   │
  │   Access-Control-Max-Age: 86400       │
  │                                       │
  │── PUT /api/users ────────────────────▶│
        
TriggerPreflight required?
Simple GET/HEAD/POST with simple headersNo
PUT/PATCH/DELETEYes
Custom headers (e.g., Authorization)Yes
Content-Type other than form-urlencoded/text/multipartYes