Session Store Low-Level Design: Session Lifecycle, Redis Storage, and Distributed Session Affinity

What a Session Store Does

A session store maintains server-side state for authenticated users across stateless HTTP requests. The client holds a session token (cookie); the server looks up the token in the store to retrieve user identity, roles, and session metadata. The design challenge is making this fast, secure, and horizontally scalable.

Session Creation and Token Generation

On successful login:

  1. Generate a cryptographically random session ID: 128 bits from /dev/urandom → base64url encode → 22-character string
  2. Store session data in the session store keyed by this ID
  3. Set the session ID as a cookie: Set-Cookie: session_id=<id>; HttpOnly; Secure; SameSite=Lax; Path=/

HttpOnly prevents JavaScript access, blocking XSS-based theft. Secure limits transmission to HTTPS. SameSite=Lax prevents CSRF on cross-site navigations while allowing top-level GET requests (needed for OAuth redirects).

Redis Hash Storage

Each session is stored as a Redis hash with an expiry:

HSET session:{session_id}
     user_id      12345
     created_at   1705324800
     ip_address   203.0.113.42
     user_agent   "Mozilla/5.0 ..."
     roles        "admin,editor"
     tenant_id    99

EXPIRE session:{session_id} 3600

Hash format allows reading individual fields (HGET) or all fields (HGETALL) without deserializing a blob. TTL is set in seconds. Redis atomically handles both the write and the expiry.

Sliding Expiry

To keep active users logged in without requiring re-authentication, reset the TTL on every authenticated request:

EXPIRE session:{session_id} 3600

This is an O(1) operation. A user active every few minutes will never see their session expire. A user who closes the browser and returns after an hour is prompted to log in again.

Absolute Session Expiry

Sliding expiry alone could keep a session alive indefinitely. For security-sensitive applications, enforce an absolute maximum lifetime. Store session_max_age = created_at + 86400 in the session data. On each request, check now() > session_max_age and force logout regardless of recent activity. This limits the window for a stolen session token.

Sticky Sessions vs Centralized Store

Two architectures for multi-server deployments:

  • Sticky sessions: Load balancer pins each client to a specific server using a cookie or IP hash. Session data lives in server memory. Simple, but any server failure loses sessions pinned to it. Also limits load balancer flexibility — can't freely route to the least-loaded server.
  • Centralized Redis store: Any server can handle any request. Session lookup is a Redis call (~0.5ms on same network). Redis can be clustered for HA. Preferred for horizontal scaling. Server restarts are transparent to users.

Centralized store is the correct default for any system expecting to scale or redeploy without coordinating sticky session draining.

Session Invalidation

  • Logout: DEL session:{session_id} — immediate invalidation, O(1)
  • Password change: Scan all session keys for the user and delete them. Store a secondary index SET user_sessions:{user_id} {session_id1} {session_id2} ... updated on session create/destroy, to avoid full scan.
  • Admin revoke: Same as logout — delete specific session ID or all sessions for a user_id using the secondary index.

Session Data Size

Keep sessions small. Store only what is needed to authorize requests: user_id, roles, tenant_id. Avoid storing large objects (user preferences, profile data) in the session — those belong in the application database and should be fetched on demand. A target session size is under 1 KB.

Concurrent Requests and CSRF Protection

Multiple browser tabs making simultaneous requests is safe: each request calls EXPIRE atomically. No race condition exists — EXPIRE on a key is serialized by Redis. The last EXPIRE wins, which is fine since all are resetting to the same TTL.

For CSRF protection, store a per-session CSRF token in the session hash. On state-changing requests (POST, PUT, DELETE), compare the X-CSRF-Token header value against the stored token. Mismatch → reject with 403.

Session Fixation Prevention

An attacker who can set a victim's session cookie before login could fix the session ID. After successful authentication, always regenerate the session ID:

  1. Copy existing session data (if any pre-auth data exists)
  2. Generate a new session ID
  3. Write session data to the new key
  4. Delete the old session key
  5. Set the new session ID in the cookie

This invalidates any session ID the attacker may have planted before the login occurred.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How is a session stored and retrieved from Redis?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “On login the server generates a cryptographically random session ID (e.g., 128-bit from /dev/urandom, base64url-encoded), serializes the session payload (user ID, roles, metadata) as JSON or MessagePack, and stores it under key 'sess:{id}' with a TTL using SET EX. On each request the session ID from the cookie is used to issue a GET, and if found the TTL is optionally reset with EXPIRE to implement sliding expiry.”
}
},
{
“@type”: “Question”,
“name”: “How does sliding expiry extend session lifetime on activity?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “After a successful session lookup, the server calls EXPIRE on the session key to reset its TTL to the full idle timeout window (e.g., 30 minutes), so the session only expires if the user is inactive for the full duration rather than from the original creation time. To avoid a Redis write on every request, the TTL reset can be skipped if the remaining TTL is still above a threshold (e.g., more than half the idle window remains).”
}
},
{
“@type”: “Question”,
“name”: “Why is a centralized session store preferred over sticky sessions?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Sticky sessions bind a user to a specific app server, so a node failure or scale-in event terminates all sessions on that node and forces re-authentication, whereas a centralized store like Redis makes sessions available to any app server instance. Centralization also simplifies horizontal scaling, canary deployments, and cross-datacenter failover since session state is not co-located with compute.”
}
},
{
“@type”: “Question”,
“name”: “How is session fixation prevented on login?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “On successful authentication the server deletes the pre-authentication session and issues a brand-new session ID, invalidating any ID that an attacker may have planted in the user's browser before login. The new session ID is sent via a Set-Cookie header with Secure, HttpOnly, and SameSite=Lax attributes to prevent transmission over plaintext and reduce CSRF exposure.”
}
}
]
}

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems

Scroll to Top