Keeping the state Clients keeping state, Cookies, Storage API


HTTP is a stateless protocol.

  • The Cookie / Set-Cookie headers, introduced by Netscape in 1996, were the Web’s first mechanism for keeping the state.
  • That same mechanism is still today the most used one.


A server keeps the state of the client between two HTTP requests (close or far apart in time)

  • Filling forms in many steps;
  • Browsing with authentication (webmail, social network, …);
  • User profiles;
  • Data in the cloud;

Keeping the state is hard…

Simulating state

HTTP does not have any native mechanism for keeping state, however it can simulate it:

HTTP headers
HTTP Authentication. (Rare in applications, mostly used in REST APIs).
GET/POST persistence
Session identifiers, CSRF protections, …
Cookies, Storage API, IndexedDB
Client-kept state.
Volatile storage on the server
Short-termed persistence: sessions (provided by the web framework), key-value stores (Memcached, Redis, …).
Persistent storage on the server
Long-termed persistence: file system, databases (SQL, NoSQL, …).

GET/POST persistence

Passing the state via the request parameters


Via the query string


Via the URL (provided by the framework router)


Via the request body (POST requests)

POST /profile HTTP/1.1


GET/POST persistence


  • Easy to deploy;
  • Robust: browsers are unlikely to break it;
  • Linkability, Searchability: data are integrated in the URL.


  • Static links must be replaced by dynamical ones (template engines can help)
  • Limited to small data.

Potential security problems

Sensitive data (passwords, etc.) must not:

  • persist in these channels.
  • transit through the URL (risks tied to copy-pasting, proxy caching, …).

Example (GET method)

app.get('/today', function(req, res) {
    n =;
    res.send(`<p>Hello ${n}, <a href='tomorrow?name=${n}'>see you tomorrow</a></p>`);
app.get('/tomorrow', function(req, res) {
    n =;
    res.send(`<p>Hello ${n}</p>`);


Key-value pairs temporarily stored by the client for a website (a domain name)

  • The server asks to store a cookie by sending a Set-Cookie header

    HTTP/1.1 200 OK
    Set-Cookie: user=foo
  • Client-side JavaScript can also instruct the browser to store a cookie (this usage is superseded by the Storage API)

    document.cookie = 'user=foo';
  • The browser sends the cookie in every request for the same domain name

    GET /app HTTP/1.1
    Cookie: user=foo
  • Cookies are stored and sent with every request until expiration.

Cookies and frameworks

In Node.js

  • Install the cookie-parser package with npm,
  • Add it as an Express middleware.
var express = require('express');
var cookieParser = require('cookie-parser');

var app = express();
app.use(cookieParser());                  // add Express middleware

app.get(..., function(req, res) {
  req.cookies.user;                       // read cookies
  res.cookie('user', 'foo');              // write a cookie
  res.clearCookie('user');                // delete a cookie

Storage API

  • Key-value store, introduced in HTML5,
  • Completely client-side API (accessed via JavaScript),
  • Surpasses the cookies limitations on data size,
  • Better guarantees on storage duration (still controlled by the client, though),
  • Two interfaces, bound to the domain name:
    • sessionStorage: lasts until the browser is closed,
    • localStorage: persistent.
if (sessionStorage['user'] === undefined) {
  sessionStorage['user'] = 'foo';
delete sessionStorage['user'];

More information: MDN on storage.

Client-kept storage


  • Cookies: session identifiers, compatibility,
  • Storage API: any application, limited storage,
  • IndexedDB: large data.


  • Light on the server, good for static websites.
  • The client can refuse to store.

Potential security problems

  • Cookie theft: session hijacking.
  • Never store a master password on the client, only ephemeral passwords should be stored (session identifiers).
Fork me on GitHub