Type-safe cookie parsing and serialization for Remix with built-in cryptographic signing, secret rotation, and complete cookie attribute control.
Installation
Features
- Secure Cookie Signing - Built-in cryptographic signing using HMAC-SHA256 to prevent cookie tampering
- Secret Rotation Support - Seamlessly rotate signing secrets while maintaining backward compatibility
- Web Standards Compliant - Built on Web Crypto API and standard cookie parsing
- Runtime Agnostic - Works in Node.js, Bun, Deno, and Cloudflare Workers
Basic Usage
import { createCookie } from 'remix/cookie'
let sessionCookie = createCookie('session', {
httpOnly: true,
secrets: ['s3cret1'],
secure: true,
})
cookie.name // "session"
cookie.httpOnly // true
cookie.secure // true
cookie.signed // true
// Parse cookie value from request
let value = await sessionCookie.parse(request.headers.get('Cookie'))
// Serialize cookie for response
let response = new Response('Hello, world!', {
headers: {
'Set-Cookie': await sessionCookie.serialize(value),
},
})
API Reference
createCookie
Creates a new cookie with the specified options.
function createCookie(
name: string,
options?: CookieOptions
): Cookie
Configuration options for the cookie.
CookieOptions
An array of secrets for signing/unsigning cookies. New secrets should be added to the beginning of the array. The first secret is used for signing, but all secrets can be used for verification (enabling secret rotation).
If true, the cookie is inaccessible to JavaScript’s Document.cookie API.Default: falseMDN Reference
sameSite
'Strict' | 'Lax' | 'None'
Controls whether the cookie is sent with cross-site requests.Default: "Lax"MDN Reference
If true, the cookie is only sent over HTTPS.Default: falseMDN Reference
If true, the cookie is partitioned (automatically sets secure: true).Default: falseMDN Reference
encode
(value: string) => string
Custom function to encode cookie values.Default: encodeURIComponent
decode
(value: string) => string
Custom function to decode cookie values.Default: decodeURIComponent
Cookie Class
Properties
The domain of the cookie.
The expiration date of the cookie.
Whether the cookie is HTTP-only. Default: false
The maximum age in seconds.
The path of the cookie. Default: "/"
sameSite
'Strict' | 'Lax' | 'None'
The SameSite attribute. Default: "Lax"
Whether the cookie is secure. Default: false
Whether the cookie is partitioned. Default: false
Whether the cookie uses cryptographic signing (true if secrets are provided).
Methods
parse
(headerValue: string | null) => Promise<string | null>
Extracts and decodes the cookie value from a Cookie header. Returns null if the cookie is not present or signature verification fails.
serialize
(value: string, props?: CookieProperties) => Promise<string>
Encodes and signs the cookie value, returning a Set-Cookie header string. Optional props can override cookie attributes for this serialization.
Examples
Basic Cookie
import { createCookie } from 'remix/cookie'
let userPrefs = createCookie('user-prefs', {
maxAge: 60 * 60 * 24 * 365, // 1 year
})
// In a request handler
let prefs = await userPrefs.parse(request.headers.get('Cookie'))
let response = new Response('Updated preferences', {
headers: {
'Set-Cookie': await userPrefs.serialize({ theme: 'dark' }),
},
})
Signed Cookies
Signing happens automatically when you provide a secrets option:
import { createCookie } from 'remix/cookie'
// Start with a single secret
let sessionCookie = createCookie('session', {
secrets: ['secret1'],
httpOnly: true,
secure: true,
sameSite: 'Lax',
})
console.log(sessionCookie.signed) // true
let response = new Response('Hello, world!', {
headers: {
'Set-Cookie': await sessionCookie.serialize({ userId: '123' }),
},
})
Secret Rotation
Rotate secrets without breaking existing cookies by adding new secrets to the beginning of the array:
let sessionCookie = createCookie('session', {
// Add new secret at the beginning
secrets: ['secret2', 'secret1'],
})
// This works for cookies signed with either secret
let value = await sessionCookie.parse(request.headers.get('Cookie'))
// Newly serialized cookies will be signed with 'secret2'
let response = new Response('Hello, world!', {
headers: {
'Set-Cookie': await sessionCookie.serialize(value),
},
})
Custom Encoding
Customize encoding/decoding for special use cases:
let sessionCookie = createCookie('session', {
// No encoding (useful for debugging, but ensure value is cookie-safe)
encode: (value) => value,
decode: (value) => value,
})
Session Cookie
Create a session cookie that expires when the browser closes:
let sessionCookie = createCookie('session', {
httpOnly: true,
secure: true,
sameSite: 'Lax',
secrets: [process.env.SESSION_SECRET],
// No maxAge or expires = session cookie
})
Overriding Attributes
Override cookie attributes when serializing:
let cookie = createCookie('user-prefs', {
maxAge: 60 * 60 * 24 * 365, // Default: 1 year
})
// Override maxAge for this specific serialization
let setCookie = await cookie.serialize(value, {
maxAge: 60 * 60, // 1 hour
})
Type Definitions
interface CookieOptions extends CookieProperties {
decode?: (value: string) => string
encode?: (value: string) => string
secrets?: string[]
}
interface CookieProperties {
domain?: string
expires?: Date
httpOnly?: boolean
maxAge?: number
path?: string
sameSite?: 'Strict' | 'Lax' | 'None'
secure?: boolean
partitioned?: boolean
}
class Cookie {
constructor(name: string, options?: CookieOptions)
readonly name: string
readonly domain: string | undefined
readonly expires: Date | undefined
readonly httpOnly: boolean
readonly maxAge: number | undefined
readonly partitioned: boolean
readonly path: string
readonly sameSite: 'Strict' | 'Lax' | 'None'
readonly secure: boolean
readonly signed: boolean
parse(headerValue: string | null): Promise<string | null>
serialize(value: string, props?: CookieProperties): Promise<string>
}
Security Considerations
Use HTTPS in Production
Always set secure: true for sensitive cookies in production to ensure they’re only sent over HTTPS:
let cookie = createCookie('session', {
secure: process.env.NODE_ENV === 'production',
secrets: [process.env.SESSION_SECRET],
})
HttpOnly for Session Cookies
Set httpOnly: true for session cookies to prevent JavaScript access:
let sessionCookie = createCookie('session', {
httpOnly: true,
secure: true,
secrets: [process.env.SESSION_SECRET],
})
SameSite Protection
Use appropriate sameSite values to protect against CSRF attacks:
'Strict' - Cookie only sent for same-site requests
'Lax' - Cookie sent for top-level navigations (default)
'None' - Cookie sent for all requests (requires secure: true)
let cookie = createCookie('session', {
sameSite: 'Strict',
secure: true,
})
- headers - HTTP header manipulation utilities (includes Cookie header classes)
- response - Response helper utilities