Skip to main content
Three feature flags in the 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

Each call is independent:
  • use_cookies() — installs tower_cookies::CookieManagerLayer. Every route gets a Cookies extractor you can read and write.
  • use_session_memory() — installs tower_sessions::SessionManagerLayer with a MemoryStore plus the cookie layer (session implies cookies). The session cookie is Secure in production environments.
  • use_session_redis(url) (feature session-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. Requires use_cookies(); the check reads the Cookies extension 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

Once use_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):
The cookie is 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

A POST without X-CSRF-Token, or with a value that doesn’t equal the cookie byte-for-byte (the comparison is constant-time), gets:
The 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 default CsrfProtectionConfig uses csrf_token / x-csrf-token. Override when your app integrates with a framework that names things differently:
The header name is a 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 on cookies 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) (feature session-redis) for multi-instance production — Redis SET/GET/DEL with PX TTL. Redis wins if both memory and Redis are configured. Pair with CSRF for browser cookie auth; Debug redacts 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-cookies and tower-sessions — the upstream crates the layers are built on; their docs cover the persistent store options for multi-process prod.