Skip to Content

CORS

Let browser apps on other origins call your API.

Browsers stop a page on one origin from reading responses from another unless the server allows it. cors adds the headers that grant that permission.

import { cors } from '@sukujs/security';

Suku.create({
  middlewares: [cors({ origin: ['https://app.example.com'], credentials: true })],
});

Register it at the app level. It then also covers 404s, errors and preflight requests for routes that have no OPTIONS handler.

CORS is not access control. It only tells browsers which pages may read a response. Other clients ignore it, and a blocked request may still have reached your server. Protect data with authentication.

Choosing origins

originAllows
'*' (default)Any site. Right for a public, unauthenticated API.
'https://app.example.com'That exact origin.
['https://a.example', /\.preview\.example$/]Listed origins and patterns.
(origin, ctx) => booleanWhatever you decide per request, sync or async.

An origin is the scheme, host and port together. https://app.example.com and http://app.example.com are different origins.

cors({ origin: (origin) => tenants.isKnownOrigin(origin) });

Credentials

Set credentials: true when the browser must send cookies or an Authorization header set by the browser itself. It requires explicit origins.

cors({ credentials: true });   // throws: "*" cannot be combined with credentials

Suku refuses that combination at startup, because browsers reject it and the usual workaround, reflecting any origin, lets every website act as your logged-in users.

Options

OptionDefaultMeaning
origin'*'Who may read responses.
credentialsfalseAllow cookies and browser-managed auth.
methodsGET, HEAD, PUT, PATCH, POST, DELETEMethods allowed cross-origin.
allowedHeadersWhat the browser asks forRequest headers the browser may send.
exposedHeadersnoneResponse headers scripts may read.
maxAge600Seconds a browser may cache a preflight.

Scripts can read only a few response headers by default. Expose your own:

cors({ origin: allowed, exposedHeaders: ['x-total-count', 'ratelimit-remaining'] });

Preflight requests

Before a request with a JSON body or custom headers, the browser sends an OPTIONS request to ask permission. cors answers it with 204 and never calls your route. An origin that is not allowed gets a 204 with no CORS headers, which makes the browser block the real request.

Caching

When the answer depends on the origin, cors adds Vary: Origin, so a shared cache does not serve one origin's response to another.

Troubleshooting

Symptom in the browserUsual cause
"No 'Access-Control-Allow-Origin' header"The page's origin is not in origin. Check scheme and port.
"Credentials flag is true, but..."credentials is on the client but not in cors.
Preflight fails with a custom headerYou set allowedHeaders and left that header out.
Works in curl, fails in the browserExpected: only browsers enforce CORS.

On this page