Cookie Serializer And Parser (RFC 6265)
Serialize browser cookies with Max-Age, Expires, Path, Domain, Secure, HttpOnly and SameSite, plus a tolerant Cookie header parser.
Code
const TOKEN = /^[!#$%&'*+\-.^_\x60|~0-9A-Za-z]+$/;
export function serializeCookie(name, value, options = {}) {
if (typeof name !== "string" || !TOKEN.test(name)) {
throw new TypeError("invalid cookie name");
}
if (typeof value !== "string") throw new TypeError("cookie value must be a string");
const encoded = encodeURIComponent(value);
if (encoded.length > 4096) throw new RangeError("cookie value is too large");
const parts = [name + "=" + encoded];
const {
maxAge,
expires,
path,
domain,
secure = false,
httpOnly = false,
sameSite,
} = options;
if (maxAge !== undefined) {
if (!Number.isInteger(maxAge)) throw new TypeError("maxAge must be an integer");
parts.push("Max-Age=" + maxAge);
}
if (expires !== undefined) {
if (!(expires instanceof Date) || Number.isNaN(expires.getTime())) {
throw new TypeError("expires must be a valid Date");
}
parts.push("Expires=" + expires.toUTCString());
}
if (path !== undefined) {
if (typeof path !== "string" || !path.startsWith("/")) {
throw new TypeError("path must start with /");
}
parts.push("Path=" + path);
}
if (domain !== undefined) {
if (typeof domain !== "string" || domain.length === 0 || /[;\s]/.test(domain)) {
throw new TypeError("invalid cookie domain");
}
parts.push("Domain=" + domain);
}
if (sameSite !== undefined) {
const normalized = String(sameSite).toLowerCase();
if (!["lax", "strict", "none"].includes(normalized)) {
throw new TypeError("sameSite must be lax, strict or none");
}
if (normalized === "none" && !secure) {
throw new TypeError("SameSite=None requires Secure");
}
parts.push("SameSite=" + normalized[0].toUpperCase() + normalized.slice(1));
}
if (secure) parts.push("Secure");
if (httpOnly) parts.push("HttpOnly");
return parts.join("; ");
}
export function parseCookieHeader(header) {
const cookies = new Map();
if (typeof header !== "string" || header.trim() === "") return cookies;
for (const part of header.split(";")) {
const equals = part.indexOf("=");
if (equals <= 0) continue;
const name = part.slice(0, equals).trim();
const raw = part.slice(equals + 1).trim();
if (!name || cookies.has(name)) continue;
try {
cookies.set(name, decodeURIComponent(raw));
} catch {
cookies.set(name, raw);
}
}
return cookies;
}Report a problem with this exampleOpens a pre-filled issue on GitHub.