Node.js CORS middleware for Connect and Express applications with comprehensive configuration options
npx @tessl/cli install tessl/npm-cors@2.8.0CORS is a Node.js middleware package for providing Cross-Origin Resource Sharing (CORS) functionality to Connect and Express applications. It enables web applications to handle cross-origin requests securely with comprehensive configuration options including origin validation, HTTP methods configuration, credential handling, and custom header management.
npm install corsconst cors = require('cors');ES6 modules:
import cors from 'cors';const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all routes with default options
app.use(cors());
// Enable CORS for a single route
app.get('/api/data', cors(), (req, res) => {
res.json({ message: 'This is CORS-enabled' });
});
// Configure CORS with custom options
const corsOptions = {
origin: 'https://example.com',
methods: ['GET', 'POST'],
credentials: true
};
app.use(cors(corsOptions));CORS is built around several key components that handle different aspects of cross-origin request processing:
Creates Express/Connect middleware that handles CORS headers for both preflight and actual requests.
/**
* Creates CORS middleware with optional configuration
* @param {Object|Function} [options] - Static configuration object or dynamic options callback
* @returns {Function} Express/Connect middleware function (req, res, next) => void
*/
function cors(options);Configure CORS behavior with a static options object.
interface CorsOptions {
/** Configures Access-Control-Allow-Origin header */
origin?: string | boolean | RegExp | Array<string | RegExp> | OriginCallback;
/** Configures Access-Control-Allow-Methods header (arrays are joined with commas) */
methods?: string | string[];
/** Configures Access-Control-Allow-Headers header (arrays are joined with commas) */
allowedHeaders?: string | string[];
/** Alias for allowedHeaders */
headers?: string | string[];
/** Configures Access-Control-Expose-Headers header (arrays are joined with commas) */
exposedHeaders?: string | string[];
/** Configures Access-Control-Allow-Credentials header */
credentials?: boolean;
/** Configures Access-Control-Max-Age header (converted to string if number) */
maxAge?: number | string;
/** Pass preflight response to next handler instead of ending */
preflightContinue?: boolean;
/** Status code for successful OPTIONS requests */
optionsSuccessStatus?: number;
}Usage Examples:
// Allow specific origin
const corsOptions = {
origin: 'https://example.com'
};
app.use(cors(corsOptions));
// Allow multiple origins using array
const corsOptions = {
origin: ['https://example1.com', 'https://example2.com']
};
app.use(cors(corsOptions));
// Allow origins matching regex pattern
const corsOptions = {
origin: /\.example\.com$/
};
app.use(cors(corsOptions));
// Configure methods and headers (arrays converted to comma-separated strings)
const corsOptions = {
origin: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'], // Becomes 'GET,POST,PUT,DELETE'
allowedHeaders: ['Content-Type', 'Authorization'], // Becomes 'Content-Type,Authorization'
credentials: true,
maxAge: 86400 // Converted to string '86400'
};
app.use(cors(corsOptions));
// Using headers alias instead of allowedHeaders
const corsOptions = {
origin: true,
headers: ['Content-Type', 'X-Custom-Header'] // Same as allowedHeaders
};
app.use(cors(corsOptions));
// Legacy browser compatibility
const corsOptions = {
optionsSuccessStatus: 200 // Some legacy browsers choke on 204
};
app.use(cors(corsOptions));Use a callback function for custom origin validation logic.
/**
* Callback function for custom origin validation
* @param {string} origin - Request origin from Origin header
* @param {Function} callback - Callback function (err, allowedOrigin) => void
* - err: Error object or null
* - allowedOrigin: The origin value to use (string, boolean, etc.)
*/
type OriginCallback = (origin: string, callback: (err: Error | null, allowedOrigin: any) => void) => void;Usage Examples:
// Whitelist specific origins
const whitelist = ['https://example1.com', 'https://example2.com'];
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, origin); // Return the origin value, not boolean
} else {
callback(new Error('Not allowed by CORS'));
}
}
};
app.use(cors(corsOptions));
// Dynamic origin validation allowing/blocking
const corsOptions = {
origin: function (origin, callback) {
if (isAllowedOrigin(origin)) {
callback(null, true); // true reflects the request origin
} else {
callback(null, false); // false disables CORS
}
}
};
app.use(cors(corsOptions));
// Custom origin transformation
const corsOptions = {
origin: function (origin, callback) {
// Transform origin or use custom logic
const allowedOrigin = transformOrigin(origin);
callback(null, allowedOrigin); // Can return string, boolean, etc.
}
};
app.use(cors(corsOptions));Use a callback function to determine CORS options dynamically per request.
/**
* Callback function for dynamic CORS options per request
* @param {Object} req - Express/Connect request object
* @param {Function} callback - Callback function (err, options) => void
*/
type OptionsCallback = (req: any, callback: (err: Error | null, options: CorsOptions) => void) => void;Usage Examples:
// Dynamic options based on request properties
const corsOptionsDelegate = function (req, callback) {
let corsOptions;
if (req.header('User-Agent') && req.header('User-Agent').includes('MyTrustedApp')) {
corsOptions = { origin: true }; // Enable CORS for trusted apps
} else {
corsOptions = { origin: false }; // Disable CORS for others
}
callback(null, corsOptions);
};
app.use(cors(corsOptionsDelegate));
// Route-specific dynamic options
const corsOptionsDelegate = function (req, callback) {
const corsOptions = {
origin: req.path.startsWith('/api/public') ? true : 'https://trusted-domain.com',
credentials: req.path.startsWith('/api/auth')
};
callback(null, corsOptions);
};
app.use(cors(corsOptionsDelegate));Enable CORS pre-flight for complex requests that require an initial OPTIONS request.
Usage Examples:
// Enable pre-flight for a specific route
app.options('/api/data', cors()); // Enable pre-flight
app.delete('/api/data', cors(), (req, res) => {
// DELETE request handler
});
// Enable pre-flight for all routes
app.options('*', cors()); // Include before other routes
// Continue processing after pre-flight
const corsOptions = {
preflightContinue: true,
optionsSuccessStatus: 200
};
app.options('/api/data', cors(corsOptions), (req, res) => {
// Additional pre-flight logic
res.sendStatus(200);
});const defaultOptions = {
origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 204
};CORS errors are passed to Express error handling middleware. Understanding error flow is important for proper CORS configuration:
// Origin callback errors - passed to Express error handler
const corsOptions = {
origin: function (origin, callback) {
if (!isValidOrigin(origin)) {
callback(new Error('Not allowed by CORS')); // Triggers error middleware
} else {
callback(null, origin); // Return allowed origin
}
}
};
// Options callback errors - also passed to Express error handler
const corsOptionsDelegate = function (req, callback) {
validateRequest(req)
.then(options => callback(null, options))
.catch(err => callback(err)); // Error passed to Express error handler
};
// Error handling middleware
app.use((err, req, res, next) => {
if (err.message === 'Not allowed by CORS') {
res.status(403).json({ error: 'CORS policy violation' });
} else {
next(err);
}
});
// Important: Returning false vs throwing error
const corsOptions = {
origin: function (origin, callback) {
if (!isValidOrigin(origin)) {
// Option 1: Return false to disable CORS (no error thrown)
callback(null, false);
// Option 2: Throw error to trigger error middleware
// callback(new Error('Not allowed by CORS'));
} else {
callback(null, true);
}
}
};CORS automatically adds Vary headers when needed to ensure proper caching:
Vary: Origin: Added when origin is dynamic (function, array, or RegExp)Vary: Access-Control-Request-Headers: Added when allowedHeaders/headers is not specified and headers are reflected from the requestWhen allowedHeaders (or headers) is not specified:
Access-Control-Request-HeadersVary: Access-Control-Request-Headers header is addedArray values are automatically converted to comma-separated strings:
methods: ['GET', 'POST'] → 'GET,POST'allowedHeaders: ['Content-Type', 'Authorization'] → 'Content-Type,Authorization'exposedHeaders: ['X-Total-Count'] → 'X-Total-Count'86400 → '86400')Preflight Requests (OPTIONS):
preflightContinue: trueSimple Requests:
/**
* CORS configuration options interface
*/
interface CorsOptions {
/**
* Configures Access-Control-Allow-Origin CORS header
* - string: Fixed origin
* - boolean: true reflects request origin, false disables CORS
* - RegExp: Pattern to test request origin
* - Array: Array of valid origins (strings or regexps)
* - Function: Custom origin validation logic
*/
origin?: string | boolean | RegExp | Array<string | RegExp> | OriginCallback;
/**
* Configures Access-Control-Allow-Methods CORS header
* String: Comma-delimited methods
* Array: Array of method strings
*/
methods?: string | string[];
/**
* Configures Access-Control-Allow-Headers CORS header
* If not specified, reflects request's Access-Control-Request-Headers
*/
allowedHeaders?: string | string[];
/** Alias for allowedHeaders */
headers?: string | string[];
/**
* Configures Access-Control-Expose-Headers CORS header
* Headers that clients can access
*/
exposedHeaders?: string | string[];
/**
* Configures Access-Control-Allow-Credentials CORS header
* Set to true to pass the header
*/
credentials?: boolean;
/**
* Configures Access-Control-Max-Age CORS header
* Cache duration for preflight requests in seconds
*/
maxAge?: number | string;
/**
* Pass CORS preflight response to next handler
* Default: false (ends response immediately)
*/
preflightContinue?: boolean;
/**
* Status code for successful OPTIONS requests
* Default: 204, set to 200 for legacy browser compatibility
*/
optionsSuccessStatus?: number;
}
/**
* Origin validation callback function
*/
type OriginCallback = (
origin: string,
callback: (err: Error | null, allowedOrigin: any) => void
) => void;
/**
* Options callback function for dynamic configuration
*/
type OptionsCallback = (
req: any,
callback: (err: Error | null, options: CorsOptions) => void
) => void;
/**
* Express/Connect middleware function signature
*/
type CorsMiddleware = (req: any, res: any, next: Function) => void;