nestrs umbrella crate cover browser-facing
state: cookies (signed cookie parsing), session (in-memory
server-side sessions via tower-sessions), session-redis (Redis-backed
sessions), and csrf (double-submit CSRF protection on unsafe methods).
All are opt-in — API-only services don’t pay for them, browser-facing apps
get the layers on the exact routes they need.
The model matches the @nestjs/csrf recipe: a csrf_token cookie is
issued on first visit, the browser sends the same value as a header on
mutations, the middleware compares them in constant time and rejects
mismatches. Sessions ride on top of cookies via tower-sessions’
SessionManagerLayer with a MemoryStore (swap to a persistent store
in production — see below).
Feature flags and builder calls
use_cookies()— installstower_cookies::CookieManagerLayer. Every route gets aCookiesextractor you can read and write.use_session_memory()— installstower_sessions::SessionManagerLayerwith aMemoryStoreplus the cookie layer (session implies cookies). The session cookie isSecurein production environments.use_session_redis(url)(featuresession-redis) — same layers with a Redis-backed store. Redis wins if both memory and Redis are set.use_csrf_protection(config)— installs the double-submit middleware. Requiresuse_cookies(); the check reads theCookiesextension populated by the cookie layer.
Read and write cookies
Cookies is the tower_cookies::Cookies extractor — pass it to a
handler and the framework injects the request-scoped cookie jar. To
set a cookie from a handler:
http_only keeps the cookie out of document.cookie (good for
session/auth), same_site=Lax is the right CSRF-resistant default for
top-level navigations, secure enforces HTTPS. Avoid same_site=None
unless you genuinely need third-party embeds.
Use the session store
Onceuse_session_memory() is on, every handler can pull a session
extractor:
Session is tower_sessions::Session — read/insert/remove typed
values, the store handles serialization. use_session_memory() is the
right choice for single-process dev and tests. For multi-process
production, enable session-redis and call
use_session_redis(url) — nestrs installs a RedisSessionStore that
uses Redis SET/GET/DEL with a PX TTL. The session cookie is
Secure when the process is running in production
(NESTRS_ENV/APP_ENV/RUST_ENV). If both memory and Redis are
configured, Redis wins and a warning is logged.
RedisSessionStore::Debug redacts user:pass@ from the URL; session
ids are never written to logs.
Issue a CSRF token on first visit
The CSRF middleware doesn’t issue tokens — it only checks the double-submit on unsafe methods. You issue the token from a handler that runs on GET (the cookie gets set, the browser keeps it):http_only: false so the SPA can read it via
document.cookie and mirror it into the X-CSRF-Token header on
every POST/PUT/PATCH/DELETE. Never log the token value; never echo it
back in the body of a response other than the issuing endpoint.
Mutations must send the header
X-CSRF-Token, or with a value that doesn’t equal the
cookie byte-for-byte (the comparison is constant-time), gets:
cookies layer is inside the CSRF layer — if you remove
use_cookies() while keeping use_csrf_protection(...), you’ll see
403 with "message": "CSRF check requires CookieManagerLayer (use NestApplication::use_cookies)" on every request. That’s by design:
the check has nothing to read without the cookie jar.
Custom cookie/header names
The defaultCsrfProtectionConfig uses csrf_token /
x-csrf-token. Override when your app integrates with a framework
that names things differently:
HeaderName (axum re-export) so you can use any
valid HTTP header — X-XSRF-TOKEN, X-CSRF-Token, or your own. The
cookie name is a &'static str because tower_cookies::Cookies::get
takes a string literal for the lookup.
The security footgun you should not ignore
If you turn oncookies or session without csrf (either by
forgetting the feature flag, or by skipping use_csrf_protection(...)),
nestrs emits a tracing::warn! at startup:
nestrs security: cookies or sessions (memory or Redis) are enabled, but CSRF protection is not configured. Cookie-authenticated browser clients remain vulnerable to cross-site request forgery on unsafe HTTP methods until you call NestApplication::use_csrf_protection(…)This isn’t a soft suggestion. Any browser-facing endpoint that mutates state behind a cookie-only session — without CSRF — is a forgeable endpoint. The warning is loud because the failure mode is silent: a malicious site can submit a form to your API on behalf of a logged-in user, and your server can’t tell the difference. Either add CSRF, or restrict the cookie layer to non-mutating routes (e.g. analytics only).
When to use what
use_cookies()alone — read-only cookies (analytics ids, locale preferences), or when your auth lives in a non-cookie scheme (Bearer tokens, mTLS).use_session_memory()for single-process dev — fast to wire, no DB, sessions disappear on restart. Replace with a persistent store before going to prod with more than one instance.use_session_redis(url)(featuresession-redis) for multi-instance production — RedisSET/GET/DELwithPXTTL. Redis wins if both memory and Redis are configured. Pair with CSRF for browser cookie auth;Debugredacts URL userinfo and never logs session ids.use_csrf_protection(...)for every browser-mutation surface — the default config is fine. Custom names only when an upstream constraint forces them.- No cookies at all — pure-API services that authenticate via Bearer tokens. Don’t pay for layers you don’t use.
See also
- Guides: security — the broader surface (Helmet headers, auth) — this guide focuses on stateful browser sessions specifically.
- Concepts: middleware-pipeline — how CSRF sits inside the cookie layer so the extractor is populated before the check.
tower-cookiesandtower-sessions— the upstream crates the layers are built on; their docs cover the persistent store options for multi-process prod.