@nextrush/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.
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 false — no 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
| Property | Type | Description |
|---|---|---|
origin | boolean | string | string[] | RegExp | OriginValidator= false | Allowed origin(s). false disables CORS, true reflects the request origin, '*' allows any origin. |
methods | string | 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. |
credentials | boolean= false | Allow cookies and authorization headers in cross-origin requests. |
maxAge? | number | Preflight cache duration in seconds. |
preflightContinue | boolean= false | Pass preflight requests to the next middleware instead of ending them. |
optionsSuccessStatus | number= 204 | HTTP status code for successful preflight responses. |
privateNetworkAccess | boolean= false | Add Access-Control-Allow-Private-Network header for local/private network requests. |
blockNullOrigin | boolean= true | Block 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$/ });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.
Related
- Middleware Overview — All middleware packages
- @nextrush/helmet — Security headers
- @nextrush/rate-limit — Rate limiting