NodeJSCookies (cookie-parser)

Cookies (cookie-parser)

Cookies are small key/value pairs the server asks the browser to store and send back on every subsequent request — the backbone of sessions, auth tokens, and preferences. Express sets cookies with the built-in res.cookie(), but reading the incoming Cookie header is easier with the cookie-parser middleware, which populates req.cookies. The real subject of this page, though, is the security attributes that separate a safe cookie from a vulnerability.

Reading cookies with cookie-parser

JS
import cookieParser from 'cookie-parser'

app.use(cookieParser())             // → req.cookies populated for every request

app.get('/dashboard', (req, res) => {
  const theme = req.cookies.theme ?? 'light'   // read an incoming cookie
  res.send(`Theme: ${theme}`)
})
`cookie-parser` only reads — `res.cookie()` writes (no package needed)
The browser sends all cookies in one `Cookie: a=1; b=2` header. `cookie-parser` splits it into the `req.cookies` object so you don't parse the string by hand. **Setting** cookies needs no middleware — `res.cookie(name, value, options)` is built into Express. So you only add `cookie-parser` for the reading side (and for signed cookies).
Setting a cookie

JS
res.cookie('theme', 'dark', {
  httpOnly: true,                 // JS can't read it (XSS defense)
  secure: true,                   // HTTPS only
  sameSite: 'lax',                // CSRF defense
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days, in milliseconds
  path: '/',
})

// Remove a cookie:
res.clearCookie('theme')
`maxAge` is in MILLISECONDS — unlike the raw Set-Cookie header
Express's `res.cookie` `maxAge` option takes **milliseconds** (it converts to the seconds the `Set-Cookie` header actually uses). Pass `60` thinking "one minute" and you get a cookie that expires in 60 ms — effectively instantly. Multiply out explicitly (`60 * 1000`) or use a duration helper.
The security attributes that matter

Attribute

What it does

Protects against

httpOnly

Hides the cookie from document.cookie

XSS token theft

secure

Only sent over HTTPS

Network sniffing

sameSite

Limits cross-site sending

CSRF

domain / path

Scopes where the cookie is sent

Over-broad exposure

maxAge / expires

Lifetime

Stale long-lived sessions

Session and auth cookies MUST be `httpOnly` + `secure` + `sameSite`
A cookie holding a session id or token without `httpOnly` can be stolen by any [XSS](/nodejs/cors) payload via `document.cookie`. Without `secure`, it leaks over plain HTTP. Without `sameSite`, it rides along on cross-site requests, enabling CSRF. For any sensitive cookie set all three: `httpOnly: true, secure: true, sameSite: 'lax'` (or `'strict'`). These are not optional hardening — they're the baseline.
sameSite values

Value

Cookie sent on cross-site requests?

strict

Never — even following a link from another site

lax

Only on top-level navigations (GET) — a good default

none

Always — but then secure: true is REQUIRED

`lax` is the sensible default; `none` demands `secure`
`lax` blocks the cross-site POSTs that drive CSRF while still sending the cookie when a user clicks a link to your site — the right balance for most apps. Use `strict` for high-value actions (banking) at the cost of some UX. `none` is only for deliberate cross-site cookies (e.g. embedded widgets) and browsers reject `SameSite=None` unless `secure` is also set.
Signed cookies — tamper detection

JS
// Provide a secret to enable signing:
app.use(cookieParser('a-long-random-secret'))

// Sign on the way out:
res.cookie('plan', 'pro', { signed: true, httpOnly: true })

// Verified on the way in — req.signedCookies, not req.cookies:
app.get('/account', (req, res) => {
  const plan = req.signedCookies.plan   // undefined if tampered/missing
  res.json({ plan })
})
Signing proves integrity, NOT confidentiality — the value is still readable
A signed cookie appends an HMAC so the server can detect if the value was altered (`req.signedCookies` returns `undefined` on a bad signature). It does **not** encrypt — anyone can still read `plan=pro` in the browser. Never store secrets in a cookie value, signed or not. For sensitive state, store only an opaque session id in the cookie and keep the data server-side (see `express-session`).
Cookies vs server-side sessions

Two strategies for remembering a user between requests:

  • Cookie holds the data — small, stateless, but the client sees it and it ships on every request. Fine for non-sensitive prefs (theme, locale).

  • Cookie holds only a session id — the actual data lives server-side (memory/Redis/DB). Standard for auth; the cookie is an opaque key. This is what express-session implements.

Keep cookies small — they're sent on every request
Every cookie scoped to a path is attached to *every* matching request, adding bytes to each one. Browsers also cap cookies (~4KB each, limited count per domain). Don't stuff JSON blobs into cookies; store an id and look the rest up server-side. Bloated cookies slow every request and can silently get dropped when limits are hit.
Next
With middleware mastered, build a complete API: [What is a REST API?](/nodejs/rest-intro).