Plugins

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

src/app.ts
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

OptionTypeDefaultDoes
origin"*" · string · string[]none (closed)Origins allowed cross-origin; lists are exact matches
methodsstring[]GET · HEAD · POST · PUT · PATCH · DELETE · OPTIONSMethods answered on preflight
allowedHeadersstring[]reflect the request's Access-Control-Request-HeadersAccess-Control-Allow-Headers on preflight
exposedHeadersstring[]omitAccess-Control-Expose-Headers on actual responses
credentialsbooleanfalseSend Access-Control-Allow-Credentials; requires an explicit origin list
maxAgenumberomitAccess-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

BehaviorDetail
PreflightAnswered by the plugin's edge handler — runs even when no flow matches the path/method
Denied preflight204 with no CORS headers — the correct, quiet failure; the browser blocks it
Credentials + *Construction throws — enumerate origins; never reflect * into credentialed access
VaryOrigin (plus request-method/headers on preflight) is appended, never duplicated
Non-HTTP triggersNo-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

On this page