How to Work with Cookies in JavaScript
Cookies are one of the oldest mechanisms for storing data in the browser. They were invented in 1994, long before localStorage, sessionStorage, or IndexedDB existed, and they remain a fundamental part of how the web works today. Every time you stay logged in to a website, accept cookie consent preferences, or see a personalized shopping experience, cookies are likely involved.
Unlike other storage mechanisms, cookies have a unique characteristic: they are automatically sent to the server with every HTTP request to the matching domain. This makes them the standard tool for authentication tokens, session identifiers, and any small piece of data that the server needs to receive with each request. But this same characteristic creates privacy concerns, performance implications, and security considerations that every developer must understand.
This guide covers what cookies are and why they exist, how to read and write them with document.cookie, every cookie option in detail (path, domain, expiration, secure, SameSite, HttpOnly), the privacy landscape around third-party cookies, and practical utility functions for working with cookies in real applications.
What Are Cookies?
A cookie is a small piece of text data that a web browser stores on the user's device. It consists of a name-value pair along with optional attributes that control its behavior (when it expires, which pages can access it, whether it requires HTTPS, etc.).
How Cookies Work
The basic lifecycle of a cookie:
- The server sends a cookie to the browser via the
Set-CookieHTTP response header. - The browser stores it on the user's device.
- On every subsequent request to the same domain, the browser automatically includes the cookie in the
CookieHTTP request header. - The server reads the cookie from the request and uses it (e.g., to identify the user's session).
Browser Server
│ │
│ ─── GET /login ────────────────────► │
│ │ (validates credentials)
│ ◄── Set-Cookie: sessionId=abc123 ── │
│ │
│ (browser stores the cookie) │
│ │
│ ─── GET /dashboard ───────────────► │
│ Cookie: sessionId=abc123 │
│ │ (recognizes the user)
│ ◄── 200 OK (personalized page) ──── │
JavaScript can also create and read cookies directly using document.cookie, without the server being involved. These are sometimes called "client-side cookies."
Common Use Cases
- Authentication: Session tokens, JWT tokens, login state
- User preferences: Language, theme, layout choices
- Analytics: Visitor tracking, A/B testing group assignment
- Shopping: Cart contents, recently viewed items
- Consent management: Recording cookie consent choices
Size and Count Limits
Cookies have strict limitations compared to other storage mechanisms:
| Limit | Value |
|---|---|
| Size per cookie | Approximately 4 KB (4096 bytes, name + value + attributes) |
| Cookies per domain | Approximately 20-50 (varies by browser; modern browsers allow ~50) |
| Total cookies | Approximately 300 across all domains |
These limits make cookies unsuitable for storing large amounts of data. For larger client-side storage needs, use localStorage (5-10 MB) or IndexedDB (hundreds of MB or more).
Cookies vs. Other Storage
| Feature | Cookies | localStorage | sessionStorage |
|---|---|---|---|
| Capacity | ~4 KB per cookie | ~5-10 MB | ~5-10 MB |
| Sent with requests | Yes (automatic) | No | No |
| Expiration | Configurable | Never (manual) | Tab close |
| Accessible from JS | Usually (unless HttpOnly) | Yes | Yes |
| Accessible from server | Yes | No | No |
| Scope | Path + domain | Origin | Origin + tab |
The key distinction: cookies are the only client-side storage mechanism that the browser sends to the server automatically.
document.cookie: Reading and Writing
JavaScript accesses cookies through the document.cookie property. This API is notoriously awkward compared to modern storage APIs, but understanding how it works is essential.
Reading Cookies
document.cookie returns a single string containing all cookies visible to the current page, formatted as semicolon-separated name=value pairs:
console.log(document.cookie);
// "username=Alice; theme=dark; lang=en; sessionId=abc123"
Key points about reading:
- All accessible cookies are in one string, separated by
;(semicolon and space). - Only the name and value are visible. Attributes like
path,domain,expires, etc. are not included indocument.cookie. - Cookies marked as
HttpOnlyby the server are not visible to JavaScript at all.
Parsing Individual Cookies
Since document.cookie returns a raw string, you need to parse it to get individual values:
// Get a specific cookie by name
function getCookie(name) {
let cookies = document.cookie.split("; ");
for (let cookie of cookies) {
let [cookieName, ...rest] = cookie.split("=");
if (cookieName === name) {
return decodeURIComponent(rest.join("="));
}
}
return null; // Cookie not found
}
// Usage
let username = getCookie("username");
console.log(username); // "Alice" or null
We use rest.join("=") instead of just taking the second part because the cookie value itself might contain = characters (for example, a base64-encoded token like token=eyJhbGci...). Splitting only on the first = ensures we capture the full value.
Writing Cookies
To set a cookie, you assign a string to document.cookie. Despite looking like a simple assignment, this does not overwrite all existing cookies. It only sets or updates the single cookie specified in the string:
// Set a single cookie
document.cookie = "username=Alice";
// This does NOT erase the "username" cookie!
// It adds a NEW cookie called "theme"
document.cookie = "theme=dark";
// Both cookies now exist
console.log(document.cookie); // "username=Alice; theme=dark"
This behavior is unique and often confusing. document.cookie acts as a getter that returns all cookies and a setter that modifies one cookie at a time.
Writing Cookies with Options
You can include attributes (options) in the cookie string:
// Set a cookie with options
document.cookie = "username=Alice; path=/; max-age=3600; secure; samesite=lax";
// Each option is separated by "; "
// Options are NOT returned when reading document.cookie
Encoding Cookie Values
Cookie values should be encoded to handle special characters like ;, =, spaces, and non-ASCII characters:
// ❌ Problem: semicolons and equals signs break the format
document.cookie = "data=name=Alice; age=30"; // This is parsed incorrectly!
// ✅ Solution: encode the value
document.cookie = "data=" + encodeURIComponent("name=Alice; age=30");
// Stored as: "data=name%3DAlice%3B%20age%3D30"
// Read it back
let value = decodeURIComponent(getCookie("data"));
console.log(value); // "name=Alice; age=30"
The cookie name should also be encoded if it might contain special characters, though it is best practice to use only alphanumeric characters, hyphens, and underscores in cookie names.
Deleting a Cookie
There is no deleteCookie() method. To delete a cookie, you set it with an expiration date in the past or a max-age of 0:
// Delete by setting max-age to 0
document.cookie = "username=; max-age=0";
// Or delete by setting an expiration in the past
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
When deleting a cookie, you must specify the same path and domain that were used when the cookie was created. If the cookie was set with path=/admin, deleting it without specifying path=/admin will not work:
// Cookie was set with path=/admin
document.cookie = "token=abc123; path=/admin";
// ❌ This does NOT delete it (wrong path)
document.cookie = "token=; max-age=0";
// ✅ This deletes it (matching path)
document.cookie = "token=; max-age=0; path=/admin";
Cookie Options
Cookie options (also called attributes) control the behavior of a cookie: when it expires, which pages can access it, whether it requires HTTPS, and how it behaves in cross-site requests.
path
The path option specifies which URL paths on the domain can access the cookie. Only pages whose URL starts with the specified path will include the cookie.
// Cookie accessible only under /admin and its sub-paths
document.cookie = "adminToken=xyz; path=/admin";
// Accessible on: /admin, /admin/users, /admin/settings
// NOT accessible on: /, /profile, /dashboard
// Cookie accessible on the entire site
document.cookie = "theme=dark; path=/";
// Accessible on ALL pages
Default behavior: If you do not specify path, the cookie defaults to the current page's path. This often causes confusion because a cookie set on /admin/settings will not be visible on /admin/users.
Best practice: Almost always set path=/ to make the cookie available across the entire site:
// Always include path=/ unless you have a specific reason not to
document.cookie = "lang=en; path=/";
domain
The domain option controls which domain (and subdomains) can access the cookie.
// Default: cookie is only accessible on the exact domain that set it
document.cookie = "token=abc123";
// If set on www.example.com, NOT accessible on blog.example.com
// Explicit domain: accessible on the domain AND all subdomains
document.cookie = "token=abc123; domain=example.com";
// Accessible on: example.com, www.example.com, blog.example.com, api.example.com
Rules and restrictions:
- You can only set
domainto the current domain or a parent domain. You cannot set cookies for a completely different domain:
// On www.example.com:
document.cookie = "x=1; domain=example.com"; // ✅ Allowed (parent domain)
document.cookie = "x=1; domain=www.example.com"; // ✅ Allowed (current domain)
document.cookie = "x=1; domain=other.com"; // ❌ Ignored (different domain)
- If no
domainis specified, the cookie is bound to the exact domain that set it (no subdomain access). This is the most restrictive and most common default.
expires and max-age
By default, cookies without an expiration are session cookies: they are deleted when the browser is closed. To make a cookie persist beyond the browser session, set expires or max-age.
expires takes a date string in UTC format:
// Expire in 7 days
let date = new Date();
date.setTime(date.getTime() + 7 * 24 * 60 * 60 * 1000);
document.cookie = `theme=dark; expires=${date.toUTCString()}; path=/`;
// The date must be in UTC format: "Thu, 01 Jan 2025 00:00:00 GMT"
console.log(date.toUTCString()); // e.g., "Fri, 22 Mar 2024 14:30:00 GMT"
max-age takes the number of seconds until expiration (simpler to use):
// Expire in 1 hour (3600 seconds)
document.cookie = "session=abc; max-age=3600; path=/";
// Expire in 30 days
document.cookie = "preferences=dark; max-age=2592000; path=/";
// 2592000 = 30 × 24 × 60 × 60
// Expire in 1 year
document.cookie = "consent=accepted; max-age=31536000; path=/";
// 31536000 = 365 × 24 × 60 × 60
// Delete immediately (max-age = 0 or negative)
document.cookie = "session=; max-age=0; path=/";
If both expires and max-age are specified, max-age takes precedence in modern browsers.
Session cookies (no expiration):
// No expires or max-age: deleted when the browser closes
document.cookie = "tempData=xyz; path=/";
"When the browser closes" does not always mean when the tab closes. Most modern browsers have session restoration features that can preserve session cookies across browser restarts. Do not rely on session cookies being deleted at any specific time.
secure
The secure flag ensures the cookie is only sent over HTTPS connections, never over plain HTTP:
// This cookie will only be sent on HTTPS requests
document.cookie = "authToken=secret123; secure; path=/";
On an HTTP page (not HTTPS), you cannot set a cookie with the secure flag. The browser will silently ignore it.
Best practice: Always use secure for any cookie containing sensitive data (tokens, session IDs, personal information). In fact, use secure for all cookies on sites served over HTTPS.
samesite
The samesite attribute controls whether the cookie is sent with cross-site requests. This is a critical security feature that helps prevent Cross-Site Request Forgery (CSRF) attacks.
samesite=strict: The cookie is never sent with cross-site requests. If a user clicks a link from another site to yours, the cookie is not included in that first request:
document.cookie = "csrf=token123; samesite=strict; path=/";
// NOT sent when clicking a link FROM another site TO your site
// NOT sent in cross-site form submissions
// NOT sent in cross-site iframes
// Only sent for same-site navigation
samesite=lax: The cookie is sent with cross-site top-level navigations (clicking a link) but not with cross-site sub-requests (images, iframes, AJAX). This is the default in modern browsers:
document.cookie = "sessionId=abc; samesite=lax; path=/";
// Sent when a user clicks a link from another site to yours
// NOT sent in cross-site iframes, images, or fetch/XHR requests
samesite=none: The cookie is sent with all requests, including cross-site. Requires the secure flag:
// Must include secure when using samesite=none
document.cookie = "trackingId=xyz; samesite=none; secure; path=/";
// Sent with ALL requests, including cross-site iframes, fetch, etc.
// Required for third-party cookie scenarios (embeds, widgets, SSO)
| SameSite Value | Cross-site links | Cross-site iframes/fetch | Requires secure? |
|---|---|---|---|
strict | Not sent | Not sent | No |
lax (default) | Sent | Not sent | No |
none | Sent | Sent | Yes |
Best practice: Use samesite=lax for most cookies. Use samesite=strict for sensitive actions (account settings, financial operations). Use samesite=none; secure only when you specifically need cross-site cookie access.
httponly
The httponly flag prevents JavaScript from accessing the cookie via document.cookie. The cookie can only be set and read by the server through HTTP headers.
// This flag CANNOT be set from JavaScript!
// It can only be set by the server via the Set-Cookie header:
// Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
When a cookie is marked HttpOnly:
document.cookiewill not include it.- JavaScript cannot read, modify, or delete it.
- It is still sent automatically with HTTP requests to the server.
This is the primary defense against Cross-Site Scripting (XSS) attacks stealing session tokens. Even if an attacker injects JavaScript into your page, they cannot access HttpOnly cookies.
Best practice: Always set HttpOnly on server-side session cookies and authentication tokens. Since HttpOnly can only be set by the server, this is a server-side configuration, not something you do in client-side JavaScript.
Complete Cookie Options Summary
// A fully configured cookie (set from JavaScript)
document.cookie = [
`${encodeURIComponent("name")}=${encodeURIComponent("value")}`,
"path=/",
"domain=example.com",
"max-age=86400", // 1 day
"secure",
"samesite=lax"
].join("; ");
// A fully configured cookie (set from the server)
// Set-Cookie: sessionId=abc123; Path=/; Domain=example.com; Max-Age=86400; Secure; HttpOnly; SameSite=Lax
| Option | Values | Default | Set via JS? |
|---|---|---|---|
path | URL path (e.g., /, /admin) | Current page path | Yes |
domain | Domain (e.g., example.com) | Current exact domain | Yes |
expires | UTC date string | Session (browser close) | Yes |
max-age | Seconds (e.g., 3600) | Session (browser close) | Yes |
secure | Flag (no value) | Not secure | Yes (HTTPS only) |
samesite | strict, lax, none | lax (in modern browsers) | Yes |
httponly | Flag (no value) | Not set | No (server only) |
Third-Party Cookies and Privacy
What Are Third-Party Cookies?
A first-party cookie is set by the website you are visiting. If you visit example.com, cookies set by example.com are first-party.
A third-party cookie is set by a different domain embedded in the page you are visiting. If example.com loads an ad from adnetwork.com, and adnetwork.com sets a cookie, that cookie is third-party.
<!-- You are on example.com -->
<!-- First-party: set by example.com -->
<script>
document.cookie = "pref=dark; path=/"; // First-party cookie
</script>
<!-- Third-party: adnetwork.com sets a cookie via its script/iframe -->
<iframe src="https://adnetwork.com/widget"></iframe>
<!-- adnetwork.com can set cookies that are "third-party" relative to example.com -->
How Third-Party Cookies Enable Tracking
Third-party cookies enable cross-site tracking:
- User visits
siteA.com, which embeds an ad fromtracker.com. tracker.comsets a cookie:userId=12345.- User visits
siteB.com, which also embeds content fromtracker.com. tracker.comreceives the sameuserId=12345cookie.tracker.comnow knows the user visited both sites and builds a profile.
This tracking happens invisibly, without the user explicitly sharing information between sites.
The End of Third-Party Cookies
Due to growing privacy concerns, third-party cookies are being phased out:
- Safari: Blocks third-party cookies by default since 2020 (Intelligent Tracking Prevention).
- Firefox: Blocks third-party cookies by default since 2019 (Enhanced Tracking Protection).
- Chrome: Plans to phase out third-party cookies (timeline has shifted, but the direction is clear).
Cookie Consent and GDPR
The EU's General Data Protection Regulation (GDPR) and the ePrivacy Directive require websites to:
- Inform users about what cookies are used and why.
- Obtain consent before setting non-essential cookies.
- Provide the ability to withdraw consent.
// Simple consent pattern
function hasConsent() {
return getCookie("cookieConsent") === "accepted";
}
function setConsent(accepted) {
if (accepted) {
document.cookie = "cookieConsent=accepted; max-age=31536000; path=/; samesite=lax";
// Now safe to set analytics and marketing cookies
initAnalytics();
} else {
document.cookie = "cookieConsent=rejected; max-age=31536000; path=/; samesite=lax";
// Do not set optional cookies
}
}
// On page load
if (getCookie("cookieConsent") === null) {
showConsentBanner();
} else if (hasConsent()) {
initAnalytics();
}
Alternatives to Third-Party Cookies
As third-party cookies disappear, alternatives are emerging:
- First-party data: Collect data directly on your own domain.
- Server-side tracking: Move tracking logic to the server.
- Privacy Sandbox APIs: Google's proposals like Topics API, Attribution Reporting.
- Contextual advertising: Target ads based on page content, not user profiles.
- First-party cookies with server-side storage: Store a first-party identifier and keep the profile data on your server.
Cookie Utility Functions
Working with the raw document.cookie string is tedious and error-prone. Here is a comprehensive set of utility functions that handle encoding, options, and edge cases:
Complete Cookie Library
const CookieUtil = {
/**
* Get a cookie value by name
* @param {string} name - Cookie name
* @returns {string|null} Cookie value or null if not found
*/
get(name) {
let encodedName = encodeURIComponent(name) + "=";
let cookies = document.cookie.split("; ");
for (let cookie of cookies) {
if (cookie.startsWith(encodedName)) {
return decodeURIComponent(cookie.substring(encodedName.length));
}
}
return null;
},
/**
* Set a cookie with options
* @param {string} name - Cookie name
* @param {string} value - Cookie value
* @param {Object} options - Cookie options
*/
set(name, value, options = {}) {
let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
// Path (default to /)
cookieString += `; path=${options.path || "/"}`;
// Domain
if (options.domain) {
cookieString += `; domain=${options.domain}`;
}
// Expiration
if (options.maxAge !== undefined) {
cookieString += `; max-age=${options.maxAge}`;
} else if (options.expires) {
let date = options.expires instanceof Date
? options.expires
: new Date(options.expires);
cookieString += `; expires=${date.toUTCString()}`;
}
// Secure
if (options.secure) {
cookieString += "; secure";
}
// SameSite
if (options.sameSite) {
cookieString += `; samesite=${options.sameSite}`;
// samesite=none requires secure
if (options.sameSite.toLowerCase() === "none" && !options.secure) {
cookieString += "; secure";
}
}
document.cookie = cookieString;
},
/**
* Delete a cookie
* @param {string} name - Cookie name
* @param {Object} options - Must match path and domain used when setting
*/
delete(name, options = {}) {
this.set(name, "", {
...options,
maxAge: 0
});
},
/**
* Check if a cookie exists
* @param {string} name - Cookie name
* @returns {boolean}
*/
has(name) {
return this.get(name) !== null;
},
/**
* Get all cookies as an object
* @returns {Object} All cookies as key-value pairs
*/
getAll() {
let result = {};
if (!document.cookie) return result;
let cookies = document.cookie.split("; ");
for (let cookie of cookies) {
let [name, ...rest] = cookie.split("=");
try {
result[decodeURIComponent(name)] = decodeURIComponent(rest.join("="));
} catch (e) {
// Skip cookies with invalid encoding
result[name] = rest.join("=");
}
}
return result;
},
/**
* Delete all accessible cookies
*/
deleteAll() {
let cookies = this.getAll();
for (let name in cookies) {
this.delete(name);
}
}
};
Using the Library
// Set cookies
CookieUtil.set("username", "Alice", {
maxAge: 86400 * 30, // 30 days
secure: true,
sameSite: "lax"
});
CookieUtil.set("theme", "dark", {
maxAge: 86400 * 365 // 1 year
});
CookieUtil.set("sessionToken", "eyJhbGciOiJIUzI1NiJ9...", {
secure: true,
sameSite: "strict"
// No maxAge → session cookie (deleted on browser close)
});
// Set with a specific expiration date
let nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + 1);
CookieUtil.set("promo", "summer2024", {
expires: nextMonth
});
// Read cookies
console.log(CookieUtil.get("username")); // "Alice"
console.log(CookieUtil.get("nonexistent")); // null
// Check existence
console.log(CookieUtil.has("theme")); // true
console.log(CookieUtil.has("missing")); // false
// Get all cookies
console.log(CookieUtil.getAll());
// { username: "Alice", theme: "dark", sessionToken: "eyJhbGci...", promo: "summer2024" }
// Delete a cookie
CookieUtil.delete("promo");
console.log(CookieUtil.has("promo")); // false
// Delete all accessible cookies
CookieUtil.deleteAll();
Convenience Functions for Common Patterns
// Store JSON data in a cookie
function setCookieJSON(name, data, options = {}) {
CookieUtil.set(name, JSON.stringify(data), options);
}
function getCookieJSON(name) {
let value = CookieUtil.get(name);
if (value === null) return null;
try {
return JSON.parse(value);
} catch (e) {
return null;
}
}
// Usage
setCookieJSON("userPrefs", {
theme: "dark",
fontSize: 16,
language: "en"
}, { maxAge: 86400 * 365 });
let prefs = getCookieJSON("userPrefs");
console.log(prefs); // { theme: "dark", fontSize: 16, language: "en" }
Practical Example: Remember Me Feature
function handleLogin(username, rememberMe) {
// Send login request to server...
if (rememberMe) {
// Persistent cookie: 30 days
CookieUtil.set("rememberUser", username, {
maxAge: 86400 * 30,
secure: true,
sameSite: "lax"
});
} else {
// Session cookie: deleted when browser closes
CookieUtil.set("rememberUser", username, {
secure: true,
sameSite: "lax"
// No maxAge/expires = session cookie
});
}
}
function handleLogout() {
CookieUtil.delete("rememberUser");
CookieUtil.delete("sessionToken");
window.location.href = "/login";
}
// On page load: check if user should be auto-logged in
function checkRememberedUser() {
let username = CookieUtil.get("rememberUser");
if (username) {
console.log(`Welcome back, ${username}!`);
// Attempt to restore session...
}
}
checkRememberedUser();
Practical Example: Cookie Consent Banner
function initCookieConsent() {
if (CookieUtil.has("cookieConsent")) {
// User has already made a choice
let consent = getCookieJSON("cookieConsent");
applyConsent(consent);
return;
}
// Show the consent banner
showConsentBanner();
}
function showConsentBanner() {
let banner = document.createElement("div");
banner.id = "cookie-banner";
banner.style.cssText = `
position: fixed; bottom: 0; left: 0; right: 0;
background: #1a1a2e; color: white; padding: 20px;
display: flex; justify-content: space-between; align-items: center;
z-index: 10000; font-family: system-ui, sans-serif;
`;
banner.innerHTML = `
<span>We use cookies to improve your experience.
<a href="/privacy" style="color: #64b5f6;">Learn more</a></span>
<div>
<button id="acceptAll" style="background: #4caf50; color: white; border: none;
padding: 10px 20px; margin: 0 4px; border-radius: 4px; cursor: pointer;">
Accept All
</button>
<button id="rejectOptional" style="background: transparent; color: white; border: 1px solid white;
padding: 10px 20px; margin: 0 4px; border-radius: 4px; cursor: pointer;">
Essential Only
</button>
</div>
`;
document.body.append(banner);
document.getElementById("acceptAll").addEventListener("click", () => {
saveConsent({ essential: true, analytics: true, marketing: true });
banner.remove();
});
document.getElementById("rejectOptional").addEventListener("click", () => {
saveConsent({ essential: true, analytics: false, marketing: false });
banner.remove();
});
}
function saveConsent(consent) {
setCookieJSON("cookieConsent", consent, {
maxAge: 86400 * 365, // Remember for 1 year
sameSite: "lax"
});
applyConsent(consent);
}
function applyConsent(consent) {
if (consent.analytics) {
// Initialize analytics (Google Analytics, etc.)
console.log("Analytics cookies enabled");
}
if (consent.marketing) {
// Initialize marketing/advertising cookies
console.log("Marketing cookies enabled");
}
}
// Initialize on page load
initCookieConsent();
Practical Example: Theme Persistence
function initTheme() {
// Check for saved theme preference
let savedTheme = CookieUtil.get("theme") || "light";
applyTheme(savedTheme);
// Theme toggle button
document.getElementById("themeToggle").addEventListener("click", () => {
let current = CookieUtil.get("theme") || "light";
let newTheme = current === "light" ? "dark" : "light";
applyTheme(newTheme);
CookieUtil.set("theme", newTheme, {
maxAge: 86400 * 365,
sameSite: "lax"
});
});
}
function applyTheme(theme) {
document.documentElement.setAttribute("data-theme", theme);
}
Debugging Cookies
function debugCookies() {
let all = CookieUtil.getAll();
let count = Object.keys(all).length;
console.group(`Cookies (${count})`);
for (let [name, value] of Object.entries(all)) {
let displayValue = value.length > 50
? value.substring(0, 50) + "..."
: value;
console.log(`${name}: ${displayValue} (${value.length} chars)`);
}
// Estimate total size
let totalSize = document.cookie.length;
console.log(`\nTotal cookie string: ${totalSize} bytes`);
if (totalSize > 4000) {
console.warn("Warning: Approaching the 4KB per-cookie limit!");
}
console.groupEnd();
}
debugCookies();
You can also inspect cookies in browser DevTools:
- Chrome: DevTools > Application > Storage > Cookies
- Firefox: DevTools > Storage > Cookies
- Safari: DevTools > Storage > Cookies
The DevTools view shows all cookie attributes (path, domain, expires, secure, HttpOnly, SameSite) that are not visible through document.cookie.
Summary
Cookies are the web's original client-side storage mechanism and remain essential for server-side communication, authentication, and user preferences.
What Cookies Are:
- Small name-value pairs (~4 KB max per cookie, ~50 per domain).
- Automatically sent to the server with every matching HTTP request.
- The only client-side storage visible to the server without explicit JavaScript.
document.cookie API:
- Reading returns all accessible cookies as one
;-separated string. - Writing sets or updates one cookie at a time (does not overwrite all cookies).
- Always
encodeURIComponentvalues when writing,decodeURIComponentwhen reading. - Delete by setting
max-age=0with the samepathanddomain.
Cookie Options:
| Option | Purpose | Best Practice |
|---|---|---|
path | Which URL paths see the cookie | Always set path=/ |
domain | Which domain and subdomains see it | Omit for current-domain-only (most secure) |
expires/max-age | When the cookie expires | Use max-age (simpler); omit for session cookies |
secure | HTTPS only | Always use on HTTPS sites |
samesite | Cross-site behavior | Use lax (default) or strict |
httponly | Block JavaScript access | Always use for auth tokens (server-side only) |
Security Best Practices:
- Use
HttpOnly(server-side) for session tokens to prevent XSS theft. - Use
Secureto prevent cookies from being sent over HTTP. - Use
SameSite=LaxorSameSite=Strictto prevent CSRF attacks. - Encode values to prevent injection.
- Set the most restrictive
pathanddomainthat your application requires.
Privacy:
- Third-party cookies are being blocked by major browsers.
- Cookie consent is legally required in many jurisdictions (GDPR, ePrivacy).
- Only set non-essential cookies after obtaining user consent.
- Consider alternatives like
localStoragefor data that does not need to go to the server.