CORS

Using Cross-Origin Resource Sharing (CORS) correctly is crucial for securing your API while allowing legitimate clients to access it. The industry standard way to handle this in Express is using the cors middleware package.

Here is a guide to the best practices, moving beyond simple wildcards to a production-ready configuration.

The Golden Rule: Avoid the Wildcard (*)

While app.use(cors()) allows requests from anywhere, this is insecure for production APIs. Best practice involves whitelisting specific domains and explicitly defining allowed methods and headers.

Best Practice Implementation

First, install the package:

Bash

npm install cors

Here is a robust, production-ready example:

JavaScript

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

// 1. Define the whitelist
// In production, these should ideally come from process.env variables
const allowedOrigins = [
  'https://www.yoursite.com',
  'https://admin.yoursite.com',
  'http://localhost:3000' // Useful for local development
];

// 2. Configure CORS options
const corsOptions = {
  origin: (origin, callback) => {
    // Check if the origin is in the whitelist
    // !origin allows requests without an Origin header (e.g., Postman, mobile apps, or server-to-server)
    if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Explicitly allow only needed methods
  allowedHeaders: ['Content-Type', 'Authorization'], // Explicitly allow only needed headers
  credentials: true, // Allow cookies/session headers to be sent
  optionsSuccessStatus: 200 // Legacy browser support (some choke on 204)
};

// 3. Apply the Middleware
// Apply globally to all routes
app.use(cors(corsOptions));

// Optional: Enable pre-flight requests for all routes
app.options('*', cors(corsOptions));

app.get('/api/data', (req, res) => {
  res.json({ message: 'This data is CORS-enabled for whitelisted domains.' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Key Breakdown of Settings

Setting

Why it matters

origin (Function)

Instead of a static array, using a function allows you to implement dynamic logic. The !origin check is vital; without it, you might accidentally block server-to-server requests or tools like Postman.

credentials: true

Crucial. If your frontend needs to send Cookies (like an Auth token) or Authorization headers, this must be set to true. Note: If this is true, you cannot set origin to *.

methods

Restricts which HTTP verbs are accepted. If your API is read-only, remove POST, PUT, and DELETE to reduce the attack surface.

allowedHeaders

Prevents clients from sending massive or malicious custom headers. Standardize on what your app actually uses (usually Content-Type and Authorization).

Handling Errors

When the callback returns an error (e.g., new Error('Not allowed by CORS')), Express will default to sending a generic HTML error page with a 500 status code.

To handle this gracefully (e.g., return a JSON response), you should add a global error handler at the end of your middleware stack:

JavaScript

Last updated