cookie Overview
The remix/cookie package provides utilities for creating, signing, parsing, and serializing HTTP cookies. Cookies are small pieces of data that the server sends to the browser, and the browser sends back with every subsequent request. They are the foundation of session management, user preferences, and other stateful behaviors on the web.
Key Concepts
- Cookie -- A key-value pair stored in the browser and sent to the server with every request. Cookies have attributes that control their behavior: when they expire, which pages they apply to, whether JavaScript can read them, and more.
- Signing -- A process that appends a cryptographic signature (HMAC-SHA256) to the cookie value. This lets the server detect if the value was tampered with. Signing does not encrypt the value -- it is still readable, but any modification will be detected.
- Secret rotation -- The practice of changing your signing key without invalidating existing cookies. You keep the old key for verification while new cookies are signed with the new key.
- Serialization -- Converting a value into a
Set-Cookieheader string that the browser can store. - Parsing -- Extracting and verifying a cookie value from an incoming
Cookieheader.
What It Does
The createCookie function returns a cookie object with two methods:
cookie.serialize(value)-- Creates aSet-Cookieheader string. If secrets are configured, the value is signed.cookie.parse(headerValue)-- Extracts this cookie's value from aCookieheader string. If secrets are configured, the signature is verified.
Quick Example
import { createCookie } from 'remix/cookie'
// Create a signed session cookie
let sessionCookie = createCookie('__session', {
httpOnly: true, // Not accessible to client-side JS
secure: process.env.NODE_ENV === 'production', // HTTPS only in production
sameSite: 'Lax', // Sent on navigation, not cross-site
secrets: [process.env.SESSION_SECRET!], // HMAC-SHA256 signing
maxAge: 60 * 60 * 24 * 7, // 1 week
})
// Serialize: create a Set-Cookie header
let setCookie = await sessionCookie.serialize('session-id-abc123')
// "__session=session-id-abc123.HmacSignature; HttpOnly; Secure; SameSite=Lax; Max-Age=604800"
// Parse: read the value from an incoming Cookie header
let value = await sessionCookie.parse(request.headers.get('Cookie') ?? '')
// "session-id-abc123" (or null if missing or tampered)Cookie Options
| Option | Type | Default | Description |
|---|---|---|---|
httpOnly | boolean | false | If true, the cookie is not accessible to JavaScript in the browser. Always set this for session cookies. |
secure | boolean | false | If true, the cookie is only sent over HTTPS. |
sameSite | 'Strict' | 'Lax' | 'None' | 'Lax' | Controls when the cookie is sent with cross-site requests. |
maxAge | number | -- | Cookie lifetime in seconds. If omitted, the cookie is deleted when the browser closes. |
domain | string | Current domain | The domain the cookie is valid for. Set to .example.com to share across subdomains. |
path | string | '/' | The URL path the cookie is valid for. |
secrets | string[] | [] | Signing keys. The first is used for signing; all are used for verification. |
partitioned | boolean | false | Enables CHIPS (third-party cookie partitioning). |
Cookie Signing
When you provide secrets, the cookie value is signed with HMAC-SHA256:
- On
serialize: the value is signed with the first secret and the signature is appended. - On
parse: all secrets are tried to verify the signature. If none match,parsereturnsnull.
This means you can detect if a cookie was modified by a user or an attacker. It does not hide the value -- if you need encryption, use cookie session storage (which encrypts the entire session).
Secret Rotation
The secrets array supports key rotation. Add the new secret at the front and keep the old one for verification:
let sessionCookie = createCookie('__session', {
secrets: [
process.env.SESSION_SECRET_NEW!, // Signs new cookies
process.env.SESSION_SECRET_OLD!, // Still verifies old cookies
],
})After enough time has passed (longer than maxAge), all cookies signed with the old secret will have expired. At that point, remove the old secret.
Common Cookie Patterns
Session cookie -- signed, HTTP-only, used with session storage:
createCookie('__session', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'Lax',
secrets: [process.env.SESSION_SECRET!],
maxAge: 60 * 60 * 24 * 7,
})Theme preference -- readable by client-side JavaScript, long-lived:
createCookie('theme', {
httpOnly: false,
sameSite: 'Lax',
maxAge: 60 * 60 * 24 * 365,
})Cross-subdomain cookie -- shared across all subdomains:
createCookie('shared', {
domain: '.example.com',
httpOnly: true,
secure: true,
sameSite: 'Lax',
secrets: [process.env.SESSION_SECRET!],
})How It Fits Together
- remix/session uses cookies (via this package) to identify which session belongs to which user.
- remix/session-middleware calls
cookie.parseon the incoming request andcookie.serializeon the outgoing response. - remix/session/cookie-storage stores session data inside the cookie itself (encrypted), using this package's cookie as the transport.
Next Steps
- Tutorial: Working with Cookies -- Create cookies, sign values, set options, implement remember-me, and rotate secrets.
- API Reference -- Complete documentation of
createCookie, cookie options, and methods.