ReferenceSecurity
@nextrush/cors

CORS

Security-hardened CORS middleware with presets and origin validation.

Browsers block cross-origin API requests by default. CORS middleware tells browsers which origins, methods, and headers your API accepts. Without it, frontend applications on different domains cannot reach your endpoints.

Source & internals

This middleware implements the full CORS specification with additional protections against null origin attacks, regex denial-of-service, and credential misconfigurations.

Default Behavior

With no options, cors() sets origin to falseno CORS headers are added and requests pass through unmodified. You must configure at least an origin value for the middleware to take effect.

The middleware always adds Vary: Origin for correct cache behavior, even when the request has no Origin header.

Installation

$ pnpm add @nextrush/cors

Minimal Usage

import { createApp } from '@nextrush/core';
import { cors } from '@nextrush/cors';

const app = createApp();

app.use(
  cors({
    origin: 'https://app.example.com',
  })
);

Configuration Options

CorsOptions

PropertyTypeDescription
originboolean | string | string[] | RegExp | OriginValidator= falseAllowed origin(s). false disables CORS, true reflects the request origin, '*' allows any origin.
methodsstring | string[]= 'GET,HEAD,PUT,PATCH,POST,DELETE'Allowed HTTP methods for preflight requests.
allowedHeaders?string | string[]Allowed request headers. If omitted, mirrors Access-Control-Request-Headers from the preflight.
exposedHeaders?string | string[]Response headers the browser can access from JavaScript.
credentialsboolean= falseAllow cookies and authorization headers in cross-origin requests.
maxAge?numberPreflight cache duration in seconds.
preflightContinueboolean= falsePass preflight requests to the next middleware instead of ending them.
optionsSuccessStatusnumber= 204HTTP status code for successful preflight responses.
privateNetworkAccessboolean= falseAdd Access-Control-Allow-Private-Network header for local/private network requests.
blockNullOriginboolean= trueBlock requests with Origin: null (from file://, data: URLs, sandboxed iframes).

Origin Types

Exact string — case-sensitive match:

cors({ origin: 'https://example.com' });

Array — whitelist of allowed origins:

cors({ origin: ['https://app.example.com', 'https://admin.example.com'] });

RegExp — pattern matching:

cors({ origin: /\.example\.com$/ });

Function — dynamic validation with async support:

cors({
  origin: async (origin, ctx) => {
    const allowed = await db.getAllowedOrigins(ctx.get('x-tenant-id'));
    return allowed.includes(origin);
  },
});

The validator receives the Origin header value and a CorsContext object. Return true to allow, false to deny, or a string to set a specific Access-Control-Allow-Origin value.

Presets

Choose a preset that matches your deployment scenario:

Allows any origin (*) without credentials. Use for public APIs or development.

import { simpleCors } from '@nextrush/cors';

app.use(simpleCors());

Requires an explicit origin. Enables credentials and blocks null origins. Caches preflight for 24 hours by default.

import { strictCors } from '@nextrush/cors';

app.use(strictCors('https://app.example.com'));

app.use(
  strictCors(['https://app.example.com', 'https://admin.example.com'], {
    exposedHeaders: ['X-Request-Id'],
  })
);

Allows localhost origins (127.0.0.1, ::1, *.localhost) with credentials and Private Network Access enabled. Null origins are allowed for file:// testing.

import { devCors } from '@nextrush/cors';

app.use(devCors());
app.use(devCors(['https://staging.example.com']));

Restricted to a list of internal service domains. Enables credentials and sets common internal headers.

import { internalCors } from '@nextrush/cors';

app.use(
  internalCors([
    'https://service1.internal.example.com',
    'https://service2.internal.example.com',
  ])
);

staticAssetsCors

Optimized for CDN and static file serving. Limits methods to GET, HEAD, OPTIONS. Caches preflight for 7 days. Defaults to '*' if no origins are specified.

import { staticAssetsCors } from '@nextrush/cors';

app.use(staticAssetsCors(['https://example.com', 'https://cdn.example.com']));

Options Builder

Build configuration with a fluent API:

import { createCorsOptions } from '@nextrush/cors';

const options = createCorsOptions()
  .allowOrigin(['https://example.com', 'https://app.example.com'])
  .allowMethods(['GET', 'POST'])
  .allowCredentials()
  .setMaxAge(86400)
  .build();

app.use(cors(options));

Integration Example

import { createApp } from '@nextrush/core';
import { cors } from '@nextrush/cors';

const app = createApp();

app.use(
  cors({
    origin: process.env.FRONTEND_URL,
    credentials: true,
    exposedHeaders: ['X-Request-Id'],
    maxAge: 86400,
  })
);

app.get('/api/users', (ctx) => ctx.json([{ id: 1, name: 'Alice' }]));

Common Mistakes

Wildcard with credentials

// ❌ Throws at configuration time
cors({ origin: '*', credentials: true });

// ✅ Use an explicit origin
cors({ origin: 'https://app.example.com', credentials: true });

Setting credentials: true with origin: '*' throws an error immediately. The CORS specification forbids this combination.

Wrong middleware order

// ❌ Auth rejects before CORS headers are set
app.use(authMiddleware);
app.use(cors({ origin: 'https://app.example.com' }));

// ✅ CORS runs first
app.use(cors({ origin: 'https://app.example.com' }));
app.use(authMiddleware);

Unsafe regex patterns

// ❌ ReDoS vulnerability — catastrophic backtracking
cors({ origin: /(.*)+\.example\.com/ });

// ✅ Safe pattern
cors({ origin: /\.example\.com$/ });

Advanced: building custom CORS logic

The source labels these exports "for advanced use cases" and "for custom implementations" — application code should reach for the presets and cors() options above first. These are exported for building your own CORS variant or unit-testing origin logic directly.

Origin validation

PropertyTypeDescription
isOriginAllowed(origin: string, option: OriginOption) => booleanCore matcher: whether an origin satisfies a configured OriginOption (string/array/RegExp/validator).
isOriginInList(origin: string, list: string[]) => booleanWhether origin exactly matches an entry in list.
isOriginMatchingPattern(origin: string, pattern: RegExp) => booleanWhether origin matches pattern, guarded by isRegexSafe.
isOriginSecure(origin: string) => booleanWhether an origin uses HTTPS (or is localhost, exempted for local dev).
isValidOriginFormat(origin: string) => booleanStructural validation of an Origin header value before any matching logic runs.
isRegexSafe(pattern: RegExp) => booleanReDoS guard — rejects patterns with catastrophic-backtracking risk before use.
createOriginCache() => Map<string, boolean>A cache the middleware uses internally to avoid re-validating the same origin on every request.
securityWarning(message: string) => voidThe internal dev-time console warning helper (e.g. for a risky origin+credentials combination).

Header helpers

PropertyTypeDescription
appendVary(ctx: Context, value: string) => voidAppends a value to the Vary header without clobbering existing values.
setVaryHeaders(ctx: Context) => voidSets the middleware's required Vary: Origin (and related) headers.
buildMethodList(methods: string | string[]) => stringNormalizes a methods option into the comma-joined header value.
normalizeHeaders(headers: string | string[]) => string[]Normalizes a headers option into an array form for comparison.
parseHeaderList(value: string) => string[]Parses a comma-separated header value (e.g. Access-Control-Request-Headers) into an array.
headerContains(list: string[], value: string) => booleanCase-insensitive membership check used when reflecting allowed headers.

Constants (CORS_HEADERS, DEFAULT_MAX_AGE, DEFAULT_METHODS, DEFAULT_OPTIONS_SUCCESS_STATUS, ORIGIN_HEADER, PREFLIGHT_INDICATORS, VARY_HEADER) are the literal header names and defaults the middleware itself uses — exported so a custom implementation stays consistent with NextRush's own naming rather than duplicating string literals.

Troubleshooting

No CORS headers in response — Verify the request includes an Origin header and that the origin matches your configuration. With origin: false (the default), no CORS headers are set.

Preflight fails — Check that the requested method and headers are allowed. If allowedHeaders is not set, the middleware reflects the request headers automatically.

Credentials rejected — Ensure origin is set to a specific value (not '*'). The browser requires an exact origin match when credentials are included.

Was this helpful?

On this page