@talosjs/middleware component ships a CorsMiddleware that reads its policy from environment variables, validates the request’s Origin, sets the matching Access-Control-* headers, and answers preflight (OPTIONS) requests. You turn CORS on by configuration, not code.
Installation
CorsMiddleware ships with @talosjs/middleware, which is already a dependency of @talosjs/app. If you depend on it directly, add it:
AppEnv from @talosjs/app-env injected by the container, so its policy is read from the same configuration your app already loads. There is nothing else to wire up.
What the middleware gives you
The whole policy comes fromCORS_* environment variables read through AppEnv. You register the middleware once and tune it per environment without redeploying code. Every request’s Origin is matched against your allowlist before any header is set, and a disallowed origin gets no CORS headers at all, so the browser blocks it. OPTIONS preflights are answered with Access-Control-Max-Age and a 204, which lets browsers cache the result and skip the preflight on later calls. Methods, headers, and a one-day max-age default to safe values, so you only set what you need to change. The middleware is a normal injectable resolved from the container; you register it through App’s dedicated cors slot, alongside your auth pipeline.
How it works
The middleware reads its configuration once at construction from the injectedAppEnv, then applies it on every request in handler.
When
CORS_ORIGINS is * (the default), the allow-origin header is sent as the literal *. When you configure a specific allowlist, the middleware reflects the request’s origin back instead of a list. That is the only spec-compliant way to allow several named origins.
Configuration
Every setting is an environment variable read throughAppEnv. List values are comma-separated.
In a Talos app these are set in
.env.yml under the cors key, which AppEnv maps onto the CORS_* variables when the app loads its environment:
.env.yml
AppEnv reads whichever is present:
.env
Registering the middleware
App takes a dedicated cors slot in its config. Pass CorsMiddleware there rather than listing it among your route middlewares:
cors slot is wired specially. The framework appends it after your route middlewares and runs it on the catch-all (/*) handler, so CORS headers land on every response, including 404s and routes short-circuited by authentication, since the pipeline runs the whole chain without breaking. That is why CORS belongs in cors, not in middlewares: a middleware listed only in middlewares never runs on unmatched routes.
With nothing configured, registering CorsMiddleware already gives you a permissive * policy, which is useful in development. Lock it down per environment by setting the cors.* keys in .env.yml (or the CORS_* variables).
How a request flows
The middleware only acts when the browser sends anOrigin header, and only when that origin is allowed.
Preflight requests
For any “non-simple” request (a custom header, or a method beyondGET/HEAD/POST) the browser first sends an OPTIONS preflight. CorsMiddleware answers it with the allow headers plus Access-Control-Max-Age, then a 204. The browser caches that result for CORS_MAX_AGE seconds and skips the preflight on subsequent identical calls, so raising CORS_MAX_AGE reduces round-trips at the cost of slower policy changes taking effect.
Getting the policy right
Use an explicitCORS_ORIGINS allowlist rather than * for any deployed environment, and reserve the wildcard for local development. Only set CORS_CREDENTIALS=true for cookie- or auth-header-based flows, and always pair it with a named-origin allowlist. List only the request headers your API actually reads in CORS_HEADERS, since a broad list weakens the intent of the policy.
Pass CorsMiddleware through the cors slot rather than middlewares: that is what makes it run on the catch-all and on rejected requests, so the browser can read errors and 404s carry the right headers. Tune CORS_MAX_AGE deliberately. A longer value cuts preflight traffic but means origin or method changes take longer to reach already-cached browsers.
Related
- Middleware — the pipeline
CorsMiddlewareruns in. - JWT — token verification middleware to register after CORS.
- Users — the user model resolved on authenticated requests.
- Configuration — how
AppEnvloads theCORS_*variables.