CORS
Official plugin — cross-origin rules at the edge: preflight OPTIONS answered even for paths bound to other methods, plus CORS headers on matched responses. Closed by default.
cors() decides which websites may call your app from a browser. It answers preflight OPTIONS requests itself — even for paths bound to other methods, which would otherwise 404 before any middleware could run — and stamps Access-Control-* headers on matched responses.
Quick start
import { oke } from "okengine";
import { cors } from "okengine/plugins";
export const app = oke({ name: "shop", env: "dev" }).plug(
cors({ origin: "https://app.example.com" }),
);Browsers on https://app.example.com can now call every flow; every other origin gets a quiet 204 on preflight with no CORS headers, so the browser blocks it. Same-origin traffic never needs this plugin — browsers only enforce CORS across origins.
Closed by default
cors() with no origin opens nothing. Cross-origin access is a deliberate decision — pass
"*", one origin, or an exact-match list when you mean it.
Options
| Option | Type | Default | Does |
|---|---|---|---|
origin | "*" · string · string[] | none (closed) | Origins allowed cross-origin; lists are exact matches |
methods | string[] | GET · HEAD · POST · PUT · PATCH · DELETE · OPTIONS | Methods answered on preflight |
allowedHeaders | string[] | reflect the request's Access-Control-Request-Headers | Access-Control-Allow-Headers on preflight |
exposedHeaders | string[] | omit | Access-Control-Expose-Headers on actual responses |
credentials | boolean | false | Send Access-Control-Allow-Credentials; requires an explicit origin list |
maxAge | number | omit | Access-Control-Max-Age seconds on preflight |
.plug(cors({
origin: ["https://app.example.com", "https://admin.example.com"],
credentials: true,
maxAge: 600,
}))cors({ origin: "*", credentials: true }) throws at construction. Browsers reject that literal
pair; reflecting the request origin would grant any site credentialed access. List exact
origins for cookies/Authorization — no any-origin + credentials shortcut.
Notes
| Behavior | Detail |
|---|---|
| Preflight | Answered by the plugin's edge handler — runs even when no flow matches the path/method |
| Denied preflight | 204 with no CORS headers — the correct, quiet failure; the browser blocks it |
Credentials + * | Construction throws — enumerate origins; never reflect * into credentialed access |
Vary | Origin (plus request-method/headers on preflight) is appended, never duplicated |
| Non-HTTP triggers | No-op |
Runtime configuration
Origin lists belong to the class of config you want to change without a redeploy — an emergency integration, a partner cutover. Pass a configSource() instead of static options and the origin rule follows the database:
const origins = configSource({
plugin: "cors",
code: { origin: "https://app.example.com" },
db: { store: db },
kv: cache,
});
on(every("30s"), origins.sync());
export const app = oke({ name: "shop", env: "dev" }).plug(cors(origins));See Plugins → Runtime configuration for the full contract.
Next
Security Headers
Official plugin — the complete secure-headers set on every HTTP response, failures included. Full helmet.js parity with API-first defaults, a CSP builder with report-only mode, and live DB-driven config.
CSRF
Official plugin — cross-site request forgery defense using fetch metadata (Sec-Fetch-Site) with an Origin fallback. Stateless: no tokens, no cookies, no session reads.