Fastify plugin that provides Cross-Origin Resource Sharing (CORS) support with configurable origin validation, preflight handling, and header management.
npx @tessl/cli install tessl/npm-fastify--cors@11.1.0@fastify/cors is a Fastify plugin that provides Cross-Origin Resource Sharing (CORS) support for web applications. It enables secure cross-origin HTTP requests by managing CORS headers including Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers with configurable options for origin validation, credential handling, and preflight request processing.
npm install @fastify/corsimport fastifyCors from '@fastify/cors';For CommonJS:
const fastifyCors = require('@fastify/cors');Named imports:
const { fastifyCors } = require('@fastify/cors');
// or
const cors = require('@fastify/cors');TypeScript imports (types):
import type {
FastifyCorsOptions,
FastifyCorsOptionsDelegate,
FastifyCorsOptionsDelegateCallback,
FastifyCorsOptionsDelegatePromise,
OriginFunction,
AsyncOriginFunction,
FastifyPluginOptionsDelegate
} from '@fastify/cors';import Fastify from 'fastify';
import cors from '@fastify/cors';
const fastify = Fastify();
// Register with default options (allows all origins)
await fastify.register(cors);
// Register with specific options
await fastify.register(cors, {
origin: ['https://example.com', 'https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
});
fastify.get('/', async (request, reply) => {
return { hello: 'world' };
});
await fastify.listen({ port: 3000 });@fastify/cors is built around several key components:
Core plugin registration function that adds CORS support to a Fastify instance.
/**
* Main CORS plugin for Fastify applications
* @param {FastifyInstance} fastify - Fastify instance
* @param {FastifyCorsOptions | FastifyCorsOptionsDelegate} options - CORS configuration options
* @param {Function} next - Fastify callback function
*/
function fastifyCors(fastify, options, next);
/**
* Alternative export patterns for different import styles
*/
const _fastifyCors; // Default export
const fastifyCors; // Named export
const default; // ES module compatibilityConfigure CORS with fixed options that apply to all requests.
interface FastifyCorsOptions {
/** Configures Access-Control-Allow-Origin header */
origin?: string | boolean | RegExp | string[] | RegExp[] | OriginFunction | AsyncOriginFunction;
/** Configures Access-Control-Allow-Methods header */
methods?: string | string[];
/** Configures Access-Control-Allow-Headers header */
allowedHeaders?: string | string[];
/** Configures Access-Control-Expose-Headers header */
exposedHeaders?: string | string[];
/** Configures Access-Control-Allow-Credentials header */
credentials?: boolean;
/** Configures Access-Control-Max-Age header */
maxAge?: number;
/** Configures Cache-Control header for preflight responses */
cacheControl?: number | string;
/** Pass preflight response to route handler */
preflightContinue?: boolean;
/** Status code for successful OPTIONS requests */
optionsSuccessStatus?: number;
/** Enable/disable preflight handling */
preflight?: boolean;
/** Enforce strict preflight header requirements */
strictPreflight?: boolean;
/** Fastify lifecycle hook to use */
hook?: FastifyCorsHook;
/** Hide OPTIONS route from documentation */
hideOptionsRoute?: boolean;
/** Log level for internal OPTIONS route */
logLevel?: LogLevel;
/** Dynamic CORS configuration function */
delegator?: FastifyCorsOptionsDelegate;
}
type FastifyCorsHook = 'onRequest' | 'preParsing' | 'preValidation' | 'preHandler' | 'preSerialization' | 'onSend';Usage Examples:
// Allow all origins (default)
await fastify.register(cors);
// Allow specific origins
await fastify.register(cors, {
origin: 'https://example.com'
});
// Allow multiple origins
await fastify.register(cors, {
origin: ['https://example.com', 'https://app.example.com']
});
// Use RegExp for origin matching
await fastify.register(cors, {
origin: /^https:\/\/.*\.example\.com$/
});
// Configure methods and headers
await fastify.register(cors, {
origin: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
});Configure origin validation using functions for dynamic logic.
/**
* Callback-based origin validation function
* @param {string | undefined} origin - Request origin header value
* @param {OriginCallback} callback - Callback function with signature (error, result)
*/
type OriginFunction = (origin: string | undefined, callback: OriginCallback) => void;
/**
* Promise-based origin validation function
* @param {string | undefined} origin - Request origin header value
* @returns {Promise<ValueOrArray<OriginType>>} Promise resolving to origin validation result
*/
type AsyncOriginFunction = (origin: string | undefined) => Promise<ValueOrArray<OriginType>>;
/**
* Origin validation callback signature
* @param {Error | null} error - Error if validation failed
* @param {ValueOrArray<OriginType>} result - Validation result
*/
type OriginCallback = (error: Error | null, result: ValueOrArray<OriginType>) => void;
type OriginType = string | boolean | RegExp;
type ValueOrArray<T> = T | T[];Usage Examples:
// Callback-based validation
await fastify.register(cors, {
origin: (origin, callback) => {
const hostname = new URL(origin).hostname;
if (hostname === 'localhost') {
callback(null, true);
return;
}
callback(new Error('Not allowed'), false);
}
});
// Promise-based validation
await fastify.register(cors, {
origin: async (origin) => {
if (!origin) return false;
const allowed = await checkOriginInDatabase(origin);
return allowed;
}
});
// Access Fastify instance via 'this'
await fastify.register(cors, {
origin: function(origin, callback) {
this.log.info(`Validating origin: ${origin}`);
callback(null, origin === 'https://trusted.com');
}
});Configure CORS options dynamically per request using delegate functions.
/**
* Callback-based delegate function for dynamic CORS options
* @param {FastifyRequest} request - Fastify request object
* @param {Function} callback - Callback with signature (error, options)
*/
interface FastifyCorsOptionsDelegateCallback {
(req: FastifyRequest, cb: (error: Error | null, corsOptions?: FastifyCorsOptions) => void): void;
}
/**
* Promise-based delegate function for dynamic CORS options
* @param {FastifyRequest} request - Fastify request object
* @returns {Promise<FastifyCorsOptions>} Promise resolving to CORS options
*/
interface FastifyCorsOptionsDelegatePromise {
(req: FastifyRequest): Promise<FastifyCorsOptions>;
}
type FastifyCorsOptionsDelegate = FastifyCorsOptionsDelegateCallback | FastifyCorsOptionsDelegatePromise;
/**
* Factory function returning delegate functions
* @param {FastifyInstance} instance - Fastify instance
* @returns {FastifyCorsOptionsDelegate} Delegate function
*/
type FastifyPluginOptionsDelegate<T = FastifyCorsOptionsDelegate> = (instance: FastifyInstance) => T;Usage Examples:
// Register with callback delegate
await fastify.register(cors, (instance) => {
return (req, callback) => {
const corsOptions = {
origin: true
};
// Disable CORS for localhost requests
if (/^localhost$/m.test(req.headers.origin)) {
corsOptions.origin = false;
}
callback(null, corsOptions);
};
});
// Register with promise delegate
await fastify.register(cors, {
delegator: async (req) => {
const userAgent = req.headers['user-agent'];
return {
origin: userAgent.includes('MyApp') ? true : false,
credentials: true
};
}
});
// Register with hook option and delegate
await fastify.register(cors, {
hook: 'preHandler',
delegator: (req, callback) => {
callback(null, {
origin: req.url.startsWith('/api/') ? '*' : false
});
}
});Disable CORS for specific routes using route configuration.
/**
* Route configuration option to disable CORS
*/
interface RouteOptions {
config?: {
cors?: boolean;
};
}Usage Examples:
// Enable CORS globally
await fastify.register(cors, { origin: '*' });
// Route with CORS enabled (default)
fastify.get('/api/public', async (request, reply) => {
return { data: 'public' };
});
// Route with CORS disabled
fastify.get('/api/internal', {
config: { cors: false }
}, async (request, reply) => {
return { data: 'internal' };
});Configure which Fastify lifecycle hook handles CORS processing.
type FastifyCorsHook =
| 'onRequest'
| 'preParsing'
| 'preValidation'
| 'preHandler'
| 'preSerialization'
| 'onSend';Usage Examples:
// Use preHandler hook instead of default onRequest
await fastify.register(cors, {
hook: 'preHandler',
origin: 'https://example.com'
});
// Use onSend hook for late CORS header injection
await fastify.register(cors, {
hook: 'onSend',
origin: '*'
});Control CORS preflight request handling behavior.
interface PreflightOptions {
/** Enable/disable preflight handling (default: true) */
preflight?: boolean;
/** Pass preflight response to route handler (default: false) */
preflightContinue?: boolean;
/** Status code for successful OPTIONS requests (default: 204) */
optionsSuccessStatus?: number;
/** Enforce strict preflight header requirements (default: true) */
strictPreflight?: boolean;
/** Hide OPTIONS route from documentation (default: true) */
hideOptionsRoute?: boolean;
/** Log level for internal OPTIONS route */
logLevel?: LogLevel;
}Usage Examples:
// Disable preflight handling
await fastify.register(cors, {
origin: '*',
preflight: false
});
// Custom preflight status for legacy browsers
await fastify.register(cors, {
origin: '*',
optionsSuccessStatus: 200
});
// Allow preflight to continue to route handler
await fastify.register(cors, {
origin: '*',
preflightContinue: true
});
// Disable strict preflight header validation
await fastify.register(cors, {
origin: '*',
strictPreflight: false
});The plugin decorates the Fastify request object with a property to track preflight handling state.
interface DecoratedFastifyRequest extends FastifyRequest {
/** Indicates if CORS preflight is enabled for this request */
corsPreflightEnabled: boolean;
}Usage Example:
fastify.addHook('onRequest', async (request, reply) => {
// Access the CORS preflight state
console.log('Preflight enabled:', request.corsPreflightEnabled);
});/**
* Main plugin callback type
*/
type FastifyCorsPlugin = FastifyPluginCallback<
NonNullable<FastifyCorsOptions> | FastifyCorsOptionsDelegate
>;
/**
* Generic value or array type
*/
type ValueOrArray<T> = T | ArrayOfValueOrArray<T>;
/**
* Array interface for recursive value or array types
*/
interface ArrayOfValueOrArray<T> extends Array<ValueOrArray<T>> {}The plugin handles various error conditions and validation failures:
// Invalid hook option
// Throws: TypeError('@fastify/cors: Invalid hook option provided.')
// Invalid origin option in delegator
// Throws: Error('Invalid CORS origin option')
// Invalid preflight request (when strictPreflight: true)
// Returns: 400 status with 'Invalid Preflight Request'
// Route not found for preflight when CORS disabled
// Calls: reply.callNotFound()Error Handling Examples:
// Handle origin validation errors
await fastify.register(cors, {
origin: (origin, callback) => {
try {
const url = new URL(origin);
if (url.protocol !== 'https:') {
callback(new Error('Only HTTPS origins allowed'), false);
return;
}
callback(null, true);
} catch (error) {
callback(new Error('Invalid origin URL'), false);
}
}
});
// Handle delegator errors
await fastify.register(cors, {
delegator: async (req) => {
try {
const config = await loadCorsConfig(req.headers.host);
return config;
} catch (error) {
throw new Error('Failed to load CORS configuration');
}
}
});