cookie vs tough-cookie vs set-cookie-parser: Cookie Handling in Node.js (2026)
TL;DR
cookie is the basic cookie parser/serializer — parses Cookie headers into objects, serializes name/value pairs with options (maxAge, path, secure), used by Express internally. tough-cookie is the full cookie jar implementation — stores cookies across requests, handles domain matching, path scoping, expiration, Set-Cookie parsing, used by got, axios, and testing tools. set-cookie-parser parses Set-Cookie response headers — extracts cookies from HTTP responses, handles multiple Set-Cookie headers, works with fetch and undici. In 2026: cookie for basic parsing/serializing, tough-cookie for cookie jar management (crawlers, testing), set-cookie-parser for reading Set-Cookie from responses.
Key Takeaways
- cookie: ~30M weekly downloads — basic parse/serialize, used by Express, zero deps
- tough-cookie: ~15M weekly downloads — full cookie jar, domain/path matching, RFC 6265
- set-cookie-parser: ~5M weekly downloads — parses Set-Cookie headers from responses
- Different scopes: cookie does parse/serialize, tough-cookie manages state, set-cookie-parser reads headers
- cookie is the lowest level — just string manipulation
- tough-cookie implements the full browser cookie algorithm
cookie
cookie — basic cookie parsing:
Parse cookies
import cookie from "cookie"
// Parse a Cookie header:
const cookies = cookie.parse("session=abc123; theme=dark; lang=en")
// → { session: "abc123", theme: "dark", lang: "en" }
// In an Express/Node.js handler:
function handler(req, res) {
const cookies = cookie.parse(req.headers.cookie || "")
const session = cookies.session
const theme = cookies.theme ?? "light"
}
Serialize cookies
import cookie from "cookie"
// Create a Set-Cookie header value:
cookie.serialize("session", "abc123", {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 60 * 60 * 24 * 7, // 7 days
path: "/",
})
// → "session=abc123; Max-Age=604800; Path=/; HttpOnly; Secure; SameSite=Strict"
// Multiple cookies — set multiple headers:
const sessionCookie = cookie.serialize("session", "abc123", { httpOnly: true })
const themeCookie = cookie.serialize("theme", "dark", { maxAge: 31536000 })
res.setHeader("Set-Cookie", [sessionCookie, themeCookie])
Cookie options
import cookie from "cookie"
// All serialization options:
cookie.serialize("name", "value", {
domain: ".pkgpulse.com", // Domain scope
path: "/api", // Path scope
maxAge: 86400, // Seconds until expiry
expires: new Date("2026-12-31"), // Absolute expiry
httpOnly: true, // No JavaScript access
secure: true, // HTTPS only
sameSite: "lax", // CSRF protection: "strict" | "lax" | "none"
priority: "high", // "low" | "medium" | "high"
partitioned: true, // CHIPS (partitioned cookies)
})
tough-cookie
tough-cookie — full cookie jar:
Cookie jar
import { CookieJar, Cookie } from "tough-cookie"
// Create a cookie jar:
const jar = new CookieJar()
// Store cookies:
await jar.setCookie("session=abc123; Path=/; HttpOnly", "https://pkgpulse.com")
await jar.setCookie("theme=dark; Path=/; Max-Age=31536000", "https://pkgpulse.com")
// Retrieve cookies for a URL:
const cookies = await jar.getCookies("https://pkgpulse.com/api/packages")
// → [Cookie { key: "session", value: "abc123" }, Cookie { key: "theme", value: "dark" }]
// Get as Cookie header string:
const cookieString = await jar.getCookieString("https://pkgpulse.com/api/packages")
// → "session=abc123; theme=dark"
Domain and path matching
import { CookieJar } from "tough-cookie"
const jar = new CookieJar()
// Domain-scoped cookie:
await jar.setCookie(
"tracking=xyz; Domain=.pkgpulse.com; Path=/",
"https://www.pkgpulse.com"
)
// Available on subdomains:
await jar.getCookieString("https://api.pkgpulse.com")
// → "tracking=xyz"
await jar.getCookieString("https://pkgpulse.com")
// → "tracking=xyz"
// NOT available on different domain:
await jar.getCookieString("https://other-site.com")
// → ""
// Path-scoped:
await jar.setCookie("admin=true; Path=/admin", "https://pkgpulse.com")
await jar.getCookieString("https://pkgpulse.com/admin/users")
// → "tracking=xyz; admin=true"
await jar.getCookieString("https://pkgpulse.com/api")
// → "tracking=xyz" (admin cookie not included)
With HTTP clients
import { CookieJar } from "tough-cookie"
import got from "got"
// Use with got:
const jar = new CookieJar()
// Login — jar stores the session cookie:
await got.post("https://api.pkgpulse.com/auth/login", {
json: { email: "user@example.com", password: "..." },
cookieJar: jar,
})
// Subsequent requests include the cookie automatically:
const response = await got("https://api.pkgpulse.com/api/me", {
cookieJar: jar,
})
// Session cookie sent automatically
Cookie inspection
import { CookieJar, Cookie } from "tough-cookie"
const jar = new CookieJar()
await jar.setCookie(
"session=abc; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600",
"https://pkgpulse.com"
)
// Inspect stored cookies:
const cookies = await jar.getCookies("https://pkgpulse.com")
const session = cookies[0]
session.key // "session"
session.value // "abc"
session.domain // "pkgpulse.com"
session.path // "/"
session.httpOnly // true
session.secure // true
session.sameSite // "strict"
session.TTL() // Time to live in ms
session.expiryDate() // Date object
set-cookie-parser
set-cookie-parser — parse Set-Cookie headers:
Parse Set-Cookie
import setCookieParser from "set-cookie-parser"
// Parse Set-Cookie header(s):
const cookies = setCookieParser.parse([
"session=abc123; Path=/; HttpOnly; Secure; SameSite=Strict",
"theme=dark; Path=/; Max-Age=31536000",
])
// → [
// { name: "session", value: "abc123", path: "/", httpOnly: true, secure: true, sameSite: "Strict" },
// { name: "theme", value: "dark", path: "/", maxAge: 31536000 },
// ]
With fetch responses
import setCookieParser from "set-cookie-parser"
// Parse cookies from a fetch response:
const response = await fetch("https://api.pkgpulse.com/auth/login", {
method: "POST",
body: JSON.stringify({ email: "user@example.com" }),
})
// Get Set-Cookie headers:
const setCookieHeaders = response.headers.getSetCookie()
// → ["session=abc; Path=/; HttpOnly", "csrf=xyz; Path=/"]
const cookies = setCookieParser.parse(setCookieHeaders)
// → [{ name: "session", value: "abc", ... }, { name: "csrf", value: "xyz", ... }]
With undici/Node.js fetch
import setCookieParser from "set-cookie-parser"
// Node.js fetch (undici) returns combined Set-Cookie:
const response = await fetch("https://api.example.com/login", {
method: "POST",
body: JSON.stringify({ user: "test" }),
})
// Split combined header:
const combinedHeader = response.headers.get("set-cookie")
const cookies = setCookieParser.splitCookiesString(combinedHeader)
// → ["session=abc; HttpOnly", "theme=dark; Max-Age=86400"]
const parsed = setCookieParser.parse(cookies)
Map format
import setCookieParser from "set-cookie-parser"
// Parse as a map (keyed by cookie name):
const cookieMap = setCookieParser.parse(setCookieHeaders, {
map: true,
})
// → {
// session: { name: "session", value: "abc", httpOnly: true, ... },
// theme: { name: "theme", value: "dark", maxAge: 31536000, ... },
// }
// Easy lookup:
const sessionValue = cookieMap.session?.value
Feature Comparison
| Feature | cookie | tough-cookie | set-cookie-parser |
|---|---|---|---|
| Parse Cookie header | ✅ | ✅ | ❌ |
| Serialize Set-Cookie | ✅ | ✅ | ❌ |
| Parse Set-Cookie | ❌ | ✅ | ✅ |
| Cookie jar | ❌ | ✅ | ❌ |
| Domain matching | ❌ | ✅ | ❌ |
| Path scoping | ❌ | ✅ | ❌ |
| Expiration tracking | ❌ | ✅ | ❌ |
| RFC 6265 compliant | Partial | ✅ | ✅ |
| HTTP client integration | ❌ | ✅ (got, axios) | ❌ |
| TypeScript | ✅ | ✅ | ✅ |
| Weekly downloads | ~30M | ~15M | ~5M |
When to Use Each
Use cookie if:
- Parsing Cookie request headers in Express/Node.js middleware
- Serializing Set-Cookie response headers
- Need basic cookie string manipulation
- Building simple cookie middleware
Use tough-cookie if:
- Building a crawler or scraper that needs cookie persistence
- Testing authenticated API flows
- Need automatic domain/path matching (browser-like behavior)
- Using with HTTP clients (got, axios) for session management
Use set-cookie-parser if:
- Reading Set-Cookie headers from fetch/undici responses
- Need to extract cookies from HTTP responses
- Building a proxy that needs to inspect Set-Cookie headers
- Working with multiple Set-Cookie headers
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on cookie v0.7.x, tough-cookie v5.x, and set-cookie-parser v2.x.