State Management in Web Applications

The Fundamental Problem

HTTP is stateless by design. Each request from a browser to a server is completely independent. The server has no built-in memory of previous requests from the same client.

Browser Server | | |---- GET /page1.html -------------->| Request 1 |<--- HTML response -----------------| | | (server forgets everything) |---- GET /page2.html -------------->| Request 2 |<--- HTML response -----------------| | | (server forgets everything) |---- POST /login ------------------>| Request 3 |<--- "Login successful" ------------| | | (server forgets everything!) |---- GET /dashboard --------------->| Request 4 |<--- "Please log in" ---------------| Who are you?

This creates a challenge: How do we build applications where users can log in, maintain shopping carts, or have personalized experiences?

Why stateless? Statelessness makes HTTP simple, scalable, and reliable. Each request can be handled by any server, making load balancing easy. But it means we must explicitly manage state ourselves.

Where State Can Live

We have three fundamental options for maintaining state:

1. Client-Side State

Data stored in the browser and sent with requests:

2. Server-Side State

Data stored on the server, indexed by some identifier:

3. Hybrid Approach (Most Common)

A small token stored client-side that points to server-side data:

Browser Server | | |---- POST /login ------------------>| | | Create session: {id: "abc123", user: "alice"} | | Store in /tmp/sessions/abc123 |<--- Set-Cookie: SESSID=abc123 -----| | | |---- GET /dashboard --------------->| | Cookie: SESSID=abc123 | | | Look up session abc123 | | Found: user is "alice" |<--- "Welcome, Alice!" -------------|

The cookie holds only an identifier (a few bytes). All actual user data stays safely on the server.

State Mechanisms Comparison

Mechanism Location Persistence Size Limit Security
URL Parameters URL (visible) Per-request only ~2KB Visible in logs, history, referrer headers
Hidden Form Fields HTML form Form submission only Unlimited User can view/modify via DevTools
Cookies Browser Configurable (session or persistent) ~4KB per cookie Sent with every request; can be HTTP-only
localStorage Browser Permanent (until cleared) ~5MB JavaScript access only; same-origin policy
sessionStorage Browser Tab lifetime ~5MB JavaScript access only; per-tab isolation
Server Sessions Server Configurable Unlimited Only session ID exposed to client
JWT Client (token) Token lifetime ~8KB practical Signed but not encrypted; depends on storage

When to Use What

URL Parameters

Best for: Shareable, bookmarkable state

Avoid for: Sensitive data (passwords, tokens), large data sets

Cookies

Best for: Data that must be sent to the server automatically

Avoid for: Large data (sent with every request)

localStorage

Best for: Persistent client-side data not needed by server

Avoid for: Sensitive data (accessible to any JavaScript on the page)

sessionStorage

Best for: Temporary state within a single tab

Avoid for: Data needed across tabs or after browser closes

Server Sessions

Best for: Secure user-specific data

Avoid for: Static data that doesn't need protection

Interactive Demos

Security Considerations

Mechanism Risks Mitigations
URL Parameters Logged in server logs, browser history, referrer headers Never put sensitive data in URLs
Cookies XSS can steal cookies; CSRF can abuse them HttpOnly flag, Secure flag, SameSite attribute
localStorage/sessionStorage Any JavaScript can read it (XSS vulnerability) Never store sensitive tokens; sanitize all inputs
Server Sessions Session fixation, session hijacking Regenerate session ID on login; use HTTPS
Hidden Form Fields User can modify values via DevTools Never trust for authorization; validate server-side
JWT XSS if in localStorage; difficult to revoke Store in httpOnly cookie; use short expiry + refresh tokens

Decision Guide

Quick decision tree:

Back to CSE 135 Home