cors review
A browser CORS error can hide a response even after your server has handled the request. cors 2.8.6 is Express/Connect middleware that sets the response headers browsers use to decide whether JavaScript may read that response. You choose origins, credential headers and preflight behavior. Authentication and authorization still belong in your application. The important setup decision is whether an origin mismatch should merely omit CORS headers or cause your own server code to reject the request.
Use cors for an Express/Connect API when you can name the browser origins that should read each response. Start with an exact origin array and check both OPTIONS and the actual route, including whether a declined origin still reaches application code.
Answers from our run
cors or @fastify/cors: which should you use?
@fastify/cors: Choose @fastify/cors when Fastify owns your server. Its npm description identifies it as Fastify CORS; check that package's own version requirements before installing. Use cors for an Express/Connect API when you can name the browser origins that should read each response.
When should you not use cors?
You need to keep unauthorized clients out of an API. CORS headers do not authenticate callers or prevent curl from making requests.
Use it if
- Your Express or Connect API needs to let a browser application on another origin read its responses.
- Public and account endpoints need different origin and credential settings, applied through middleware or a per-request options callback.
- You need explicit control over OPTIONS responses, allowed request headers or response headers exposed to browser JavaScript.
- You want one reviewed middleware policy rather than repeating header assignments in route handlers.
- You need to keep unauthorized clients out of an API. CORS headers do not authenticate callers or prevent curl from making requests.
- You expect an origin allowlist to reject the request with an HTTP error. A false origin callback result calls the next handler without adding CORS headers.
- Your application uses Fastify or Koa. Start with the CORS package for that framework instead of assuming Connect middleware fits its lifecycle.
- You plan to reflect every origin with credentials enabled. That configuration gives every requesting origin the same header permission, defeating a selective origin policy.
- You need only server-to-server access. Those clients do not enforce browser CORS rules.
Setup reality
Install the selected release with npm install cors@2.8.6 in an existing Express application. cors 2.8.6 declares object-assign and vary as runtime dependencies. This guide is based on the release-pinned documentation and implementation. We did not install the package, run these snippets or test a browser integration. The snippets are separate configuration illustrations: after the first example, app and cors refer to the initialized Express application and imported middleware. Pick the settings you need rather than stacking every example.
The default cors() policy emits a wildcard allow-origin header and finishes OPTIONS requests with status 204. That is a deliberate choice for public responses. For a named frontend, start with an array of exact origins, including the scheme and any non-default port. The implementation treats a fixed origin string differently: it writes that string even when the incoming Origin differs. An array checks membership before writing the allow-origin value. A browser may decline to expose the response in either mismatch case, but your route can still execute.
An origin callback receives the request origin and a Node-style callback. Returning callback(null, false) continues downstream without CORS headers. Passing an error calls next(error). If you need a server-side rejection, implement that requirement explicitly alongside authentication and authorization. Do not treat an origin header as proof of caller identity. Requests without an Origin header reach the callback with undefined, so decide what that means for your application's non-browser clients.
Install CORS middleware before routes that need its headers. Application-level middleware already handles preflights. If you attach it only to a GET or DELETE route, add an OPTIONS handler on the same explicit path. The pinned README contains app.del and a wildcard route example. These illustrations use app.delete and an explicit route path instead, without claiming they were tested against every Express release. Check your installed router before copying route syntax.
credentials: true emits Access-Control-Allow-Credentials. Pair it with a reviewed origin allowlist. The implementation does not reject the combination of wildcard origin and credentials for you. CORS middleware does not configure sessions or implement CSRF protection. Keep those controls in the application's security design. Dynamic origin policies also emit Vary: Origin, which matters when reviewing how your cache handles responses.
Scores below are editorial judgments from the cited documentation and source: 1 means poor fit, 2 means substantial gaps, 3 means usable with stated caveats, 4 means well supported within the inspected scope, and 5 means unusually complete evidence for that dimension. API stability assesses the documented surface and change history. Docs quality assesses coverage and copying hazards. Maintenance assesses recorded release work, while ecosystem assesses framework fit and integration boundaries. These scores do not measure security, runtime compatibility or adoption.
Patterns
Start with an exact origin array allow-exact-origins
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors({ origin: ['https://app.example.com'] }))
app.get('/api/items', (req, res) => res.json([]))
app.listen(3000)Documentation-based illustration, not executed. An array checks the incoming origin. A fixed string writes that origin value even for a mismatching request. Neither configuration rejects the actual route by itself.
Allow public browser reads allow-public-reads
app.use(cors())This emits Access-Control-Allow-Origin: * and handles OPTIONS by default. Use it deliberately for public responses. Do not add it ahead of a selective policy and assume the later policy removes earlier headers.
Scope middleware to an API prefix scope-api-prefix
app.use('/api', cors({ origin: ['https://app.example.com'] }))Register this before the API routes. It applies to requests that reach the mounted middleware, including OPTIONS. It does not turn off CORS headers written elsewhere.
Use the same policy for preflight and DELETE handle-route-options
const itemCors = cors({
origin: ['https://app.example.com'],
methods: ['DELETE']
})
app.options('/items/:id', itemCors)
app.delete('/items/:id', itemCors, (req, res) => {
res.json({ id: req.params.id })
})The response only echoes an ID; it does not delete stored data. methods sets a preflight response header. It does not enforce which HTTP methods your server accepts. Authentication belongs on the actual route.
Decline an origin without raising an error decide-origin-callback
const approved = new Set(['https://app.example.com'])
app.use(cors({
origin(origin, callback) {
callback(null, approved.has(origin))
}
}))A false callback result calls next without adding CORS headers. The route may still run. This example also declines undefined, the value used when no Origin header is present. Use separate application logic if rejection is required.
Emit credentials headers for selected origins enable-credentials
app.use(cors({
origin: ['https://app.example.com'],
credentials: true
}))credentials: true emits the credentials header. The library does not validate wildcard-plus-credentials configuration, configure cookies or provide CSRF defense. Keep the allowlist explicit and review the session design separately.
Declare the request headers your frontend needs set-request-headers
app.use(cors({
origin: ['https://app.example.com'],
allowedHeaders: ['Content-Type', 'Authorization']
}))Without allowedHeaders, the implementation reflects Access-Control-Request-Headers and adds that field to Vary. Declaring Authorization here permits the browser header through CORS negotiation; it does not validate a token.
Expose a pagination header to JavaScript expose-response-header
app.use(cors({
origin: ['https://app.example.com'],
exposedHeaders: ['X-Total-Count']
}))
app.get('/items', (req, res) => {
res.setHeader('X-Total-Count', '0')
res.json([])
})exposedHeaders names response headers the browser may expose. It does not create their values. Here the route sets a count matching its empty illustrative result.
Set a preflight cache header set-preflight-age
app.use(cors({
origin: ['https://app.example.com'],
maxAge: 600
}))This writes Access-Control-Max-Age: 600 on OPTIONS responses. The implementation also emits a value of 0 when maxAge is 0. This setting does not configure caching for the actual JSON response.
Let a later handler finish OPTIONS continue-preflight
const continuedCors = cors({
origin: ['https://app.example.com'],
preflightContinue: true
})
app.options('/items', continuedCors, (req, res) => {
res.sendStatus(200)
})preflightContinue calls the next handler after applying CORS headers. A later handler must finish the response. If you only need a different automatic success status, set optionsSuccessStatus: 200 and leave preflightContinue false.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @fastify/cors | npm | Choose @fastify/cors when Fastify owns your server. Its npm description identifies it as Fastify CORS; check that package's own version requirements before installing. |
| @koa/cors | npm | Choose @koa/cors when Koa owns your server. Its npm description identifies CORS for Koa; this guide does not compare its options or performance. |
More infra guides
semver · boto3 · opentelemetry-api · @opentelemetry/api · psutil · distro · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.