cookie-parser
HTTP cookies arrive on the request as a single Cookie header — a semicolon-delimited string that Express does not parse for you out of the box. The cookie-parser middleware decodes that header into a convenient req.cookies object, and, when you supply a secret, it can verify tamper-proof signed cookies so a client cannot quietly edit values you set. It is the standard companion to session handling, “remember me” flows, and any feature that needs to round-trip small pieces of state through the browser.
Installing and mounting cookie-parser
Install the package and register it with app.use before any route that needs to read cookies. The middleware runs once per request, parses the Cookie header, and attaches the result to req.cookies.
npm install cookie-parser
const express = require("express");
const cookieParser = require("cookie-parser");
const app = express();
app.use(cookieParser()); // no secret -> unsigned cookies only
app.get("/whoami", (req, res) => {
res.json({ cookies: req.cookies });
});
app.listen(3000, () => console.log("Listening on http://localhost:3000"));
When a browser sends Cookie: theme=dark; lang=en, the parsed object is ready on the request:
Output:
> GET /whoami HTTP/1.1
> Cookie: theme=dark; lang=en
< HTTP/1.1 200 OK
< Content-Type: application/json
{"cookies":{"theme":"dark","lang":"en"}}
Reading and setting cookies
req.cookies is a plain object keyed by cookie name. To create a cookie you use Express’s built-in res.cookie(name, value, options) — that part is core Express, not cookie-parser — and to remove one you call res.clearCookie(name). The options control lifetime and security flags.
app.get("/login", async (req, res) => {
// res.cookie comes from Express itself
res.cookie("theme", "dark", {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days in ms
});
res.json({ ok: true });
});
app.get("/theme", (req, res) => {
const theme = req.cookies.theme ?? "light";
res.json({ theme });
});
The most common cookie options are summarized below.
| Option | Type | Purpose |
|---|---|---|
maxAge | number | Lifetime in milliseconds (relative) |
expires | Date | Absolute expiry date |
httpOnly | boolean | Hide the cookie from client-side JavaScript |
secure | boolean | Send only over HTTPS |
sameSite | "strict" / "lax" / "none" | CSRF / cross-site sending policy |
signed | boolean | Sign the cookie (requires a secret) |
path | string | URL path the cookie applies to |
domain | string | Domain the cookie is scoped to |
Always set
httpOnly: truefor cookies that hold session or auth data. It preventsdocument.cookiefrom reading them, which closes off a large class of XSS-based token theft.
Signed cookies
Pass a secret to cookieParser(secret) to enable signing. A signed cookie stores the value plus an HMAC of that value using your secret. If a client edits the value, the signature no longer matches and Express discards it — so signed cookies are about integrity, not secrecy (the value is still readable, just not forgeable).
Signed cookies are written with signed: true and read from req.signedCookies rather than req.cookies.
const cookieParser = require("cookie-parser");
app.use(cookieParser(process.env.COOKIE_SECRET));
app.get("/set", (req, res) => {
res.cookie("uid", "user-42", {
signed: true,
httpOnly: true,
sameSite: "lax",
});
res.json({ ok: true });
});
app.get("/account", (req, res) => {
const uid = req.signedCookies.uid; // undefined if tampered or missing
if (!uid) return res.status(401).json({ error: "not signed in" });
res.json({ uid });
});
On the wire a signed cookie carries an s: prefix and the appended signature:
Output:
< Set-Cookie: uid=s%3Auser-42.k8Hk%2Fd1c3...; Path=/; HttpOnly; SameSite=Lax
If a request arrives with a uid whose value was altered, req.signedCookies.uid comes back as the boolean false, while a clean, verified cookie yields its original string. You can also rotate secrets by passing an array — the first is used to sign, and any in the list can verify:
app.use(cookieParser(["new-secret", "previous-secret"]));
Clearing cookies
To delete a cookie, call res.clearCookie(name, options). The browser only honors the deletion if the path and domain match those used when the cookie was set, so pass the same options you originally used.
app.post("/logout", (req, res) => {
res.clearCookie("uid", { httpOnly: true, sameSite: "lax" });
res.clearCookie("theme");
res.status(204).end();
});
clearCookie works by sending a Set-Cookie header with an expiry date in the past:
Output:
< Set-Cookie: uid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly
cookie-parser works unchanged on Express 4 and Express 5 — it is a standalone middleware and does not rely on the routing changes (such as the new wildcard syntax) that 5.x introduced.
Best Practices
- Mount
cookieParser()early, before any route or middleware that readsreq.cookiesorreq.signedCookies. - Store the signing secret in an environment variable; never hard-code it or commit it to source control.
- Use signed cookies for anything a client must not forge (user ids, roles), and read them from
req.signedCookies, notreq.cookies. - Remember signing is integrity, not encryption — do not place truly secret data in a cookie value even when signed.
- Set
httpOnly,secure, and an appropriatesameSiteon every auth-related cookie to defend against XSS and CSRF. - Pass the same
path/domaintores.clearCookiethat you used inres.cookie, or the browser will ignore the deletion. - Rotate secrets by supplying an array to
cookieParser, keeping old secrets long enough to verify existing cookies.