Labs ICT
Pro Login

Cookies

Cookies are small pieces of data that websites store on a user's browser. They're used to maintain state between HTTP requests, track user sessions, and store user preferences. Unlike localStorage, cookies are automatically sent to the server with each HTTP request, making them essential for server-side session management.

In this tutorial, we'll explore cookie creation, manipulation, security considerations, modern alternatives, and best practices for working with cookies in JavaScript.

Understanding Cookies

Cookies are key-value pairs stored in the browser with specific attributes and limitations.

Cookie Basics

// Cookie characteristics
// - Sent to server with each HTTP request
// - Limited size (typically 4KB per cookie)
// - Limited number per domain (usually 20-50)
// - Can have expiration dates
// - Can be restricted to specific paths or domains
// - Can be marked as secure or HTTP-only

// Check if cookies are enabled
function areCookiesEnabled() {
  try {
    document.cookie = 'testcookie=1';
    const enabled = document.cookie.indexOf('testcookie=') !== -1;
    document.cookie = 'testcookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
    return enabled;
  } catch (e) {
    return false;
  }
}

console.log('Cookies enabled:', areCookiesEnabled());

// Basic cookie format
// name=value; expires=date; path=path; domain=domain; secure; HttpOnly; SameSite=Lax/Strict/None

// Example cookie string
document.cookie = 'username=john_doe; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/; domain=example.com; secure; SameSite=Lax';

Cookie Attributes

// Expires - When cookie expires
// Session cookie (expires when browser closes)
document.cookie = 'session_data=value; path=/';

// Persistent cookie (expires at specific date)
const expirationDate = new Date();
expirationDate.setFullYear(expirationDate.getFullYear() + 1);
document.cookie = `user_id=123; expires=${expirationDate.toUTCString()}; path=/`;

// Max-Age - Seconds until expiration
document.cookie = 'temp_data=value; max-age=3600; path=/'; // Expires in 1 hour

// Path - Restrict to specific path
document.cookie = 'page_pref=value; path=/admin'; // Only accessible on /admin path
document.cookie = 'site_pref=value; path=/'; // Accessible on entire site

// Domain - Restrict to specific domain
document.cookie = 'subdomain=value; domain=blog.example.com'; // Only blog subdomain
document.cookie = 'sitedata=value; domain=example.com'; // All subdomains

// Secure - Only send over HTTPS
document.cookie = 'sensitive=value; secure; path=/'; // Only over HTTPS

// HttpOnly - Not accessible via JavaScript (server-side only)
// This can only be set by server, not JavaScript

// SameSite - CSRF protection
document.cookie = 'token=value; SameSite=Strict; path=/'; // Strict
document.cookie = 'pref=value; SameSite=Lax; path=/'; // Lax (default)
document.cookie = 'analytics=value; SameSite=None; path=/; secure'; // None (requires secure)

Reading and Writing Cookies

Methods for creating, reading, and manipulating cookies in JavaScript.

Setting Cookies

// Basic cookie setting
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 = name + '=' + value + expires + '; path=/';
}

// Usage
setCookie('username', 'john_doe', 30);
setCookie('theme', 'dark', 7);

// Advanced cookie setting with options
function setCookieAdvanced(name, value, options = {}) {
  const {
    expires = null,
    maxAge = null,
    domain = null,
    path = '/',
    secure = false,
    sameSite = 'Lax'
  } = options;
  
  let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
  
  if (expires) {
    cookieString += `; expires=${expires.toUTCString()}`;
  }
  
  if (maxAge) {
    cookieString += `; max-age=${maxAge}`;
  }
  
  if (domain) {
    cookieString += `; domain=${domain}`;
  }
  
  cookieString += `; path=${path}`;
  
  if (secure) {
    cookieString += '; secure';
  }
  
  cookieString += `; SameSite=${sameSite}`;
  
  document.cookie = cookieString;
}

// Usage
setCookieAdvanced('auth_token', 'abc123', {
  expires: new Date('2023-12-31'),
  secure: true,
  sameSite: 'Strict'
});

// Set multiple cookies at once
function setMultipleCookies(cookies) {
  Object.entries(cookies).forEach(([name, value]) => {
    setCookie(name, value, 30);
  });
}

setMultipleCookies({
  'theme': 'dark',
  'language': 'en',
  'timezone': 'UTC'
});

Reading Cookies

// Basic cookie reading
function getCookie(name) {
  const nameEQ = name + '=';
  const cookies = document.cookie.split(';');
  
  for (let i = 0; i < cookies.length; i++) {
    let cookie = cookies[i];
    
    while (cookie.charAt(0) === ' ') {
      cookie = cookie.substring(1, cookie.length);
    }
    
    if (cookie.indexOf(nameEQ) === 0) {
      return decodeURIComponent(cookie.substring(nameEQ.length, cookie.length));
    }
  }
  
  return null;
}

// Usage
const username = getCookie('username');
const theme = getCookie('theme');

console.log('Username:', username);
console.log('Theme:', theme);

// Get all cookies as object
function getAllCookies() {
  const cookies = {};
  
  if (document.cookie) {
    document.cookie.split(';').forEach(cookie => {
      const [name, value] = cookie.trim().split('=');
      if (name && value) {
        cookies[decodeURIComponent(name)] = decodeURIComponent(value);
      }
    });
  }
  
  return cookies;
}

// Usage
const allCookies = getAllCookies();
console.log('All cookies:', allCookies);
console.log('User preferences:', {
  theme: allCookies.theme,
  language: allCookies.language
});

// Check if cookie exists
function hasCookie(name) {
  return getCookie(name) !== null;
}

// Usage
if (hasCookie('user_session')) {
  console.log('User is logged in');
} else {
  console.log('User is not logged in');
}

Deleting Cookies

Methods for removing cookies from the browser.

Basic Deletion

// Delete cookie by setting expiration in the past
function deleteCookie(name) {
  document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
}

// Usage
deleteCookie('username');
deleteCookie('theme');

// Delete cookie with specific path
function deleteCookieWithPath(name, path) {
  document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=' + path;
}

// Delete cookie with specific domain
function deleteCookieWithDomain(name, domain) {
  document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=' + domain;
}

// Delete all cookies
function deleteAllCookies() {
  const cookies = document.cookie.split(';');
  
  cookies.forEach(cookie => {
    const [name] = cookie.trim().split('=');
    if (name) {
      deleteCookie(decodeURIComponent(name));
    }
  });
}

// Usage
deleteAllCookies();

// Delete cookies matching pattern
function deleteCookiesByPattern(pattern) {
  const cookies = getAllCookies();
  
  Object.keys(cookies).forEach(name => {
    if (pattern.test(name)) {
      deleteCookie(name);
    }
  });
}

// Delete all analytics cookies
deleteCookiesByPattern(/^ga_/);
// Delete all temporary cookies
deleteCookiesByPattern(/^temp_/);

Advanced Deletion

// Delete cookie with all original attributes
function deleteCookieAdvanced(name, originalOptions = {}) {
  const {
    domain = null,
    path = '/',
    secure = false,
    sameSite = 'Lax'
  } = originalOptions;
  
  let cookieString = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
  
  if (domain) {
    cookieString += `; domain=${domain}`;
  }
  
  cookieString += `; path=${path}`;
  
  if (secure) {
    cookieString += '; secure';
  }
  
  cookieString += `; SameSite=${sameSite}`;
  
  document.cookie = cookieString;
}

// Store original cookie options when setting
const cookieOptions = new Map();

function setCookieWithOptions(name, value, options = {}) {
  // Store options for later deletion
  cookieOptions.set(name, options);
  
  setCookieAdvanced(name, value, options);
}

function deleteCookieWithOptions(name) {
  const options = cookieOptions.get(name) || {};
  deleteCookieAdvanced(name, options);
}

// Usage
setCookieWithOptions('secure_token', 'abc123', {
  domain: '.example.com',
  path: '/api',
  secure: true,
  sameSite: 'Strict'
});

deleteCookieWithOptions('secure_token');

Cookie Security

Important security considerations when working with cookies.

Security Attributes

// Secure flag - HTTPS only
function setSecureCookie(name, value) {
  document.cookie = `${name}=${value}; secure; path=/`;
}

// HttpOnly flag - Server-side only (cannot be set via JavaScript)
// This must be set by server HTTP header:
// Set-Cookie: sessionid=abc123; HttpOnly; Secure

// SameSite flag - CSRF protection
// Strict: Cookie only sent with same-site requests
setCookie('csrf_token', 'token', {
  secure: true,
  sameSite: 'Strict'
});

// Lax: Cookie sent with same-site requests and top-level navigation
setCookie('preferences', 'dark', {
  sameSite: 'Lax'
});

// None: Cookie sent with all requests (requires Secure)
setCookie('tracking_id', '12345', {
  secure: true,
  sameSite: 'None'
});

// Content Security Policy for cookies
// Set in HTTP header:
// Content-Security-Policy: cookie-src 'self'; script-src 'self'

Data Protection

// Encode cookie values
function setSecureCookie(name, value, options = {}) {
  // Always encode values
  const encodedValue = encodeURIComponent(value);
  const encodedName = encodeURIComponent(name);
  
  let cookieString = `${encodedName}=${encodedValue}`;
  
  // Add security attributes
  if (options.secure !== false) {
    cookieString += '; secure';
  }
  
  if (options.httpOnly) {
    // Note: HttpOnly cannot be set via JavaScript
    console.warn('HttpOnly cannot be set via JavaScript');
  }
  
  cookieString += `; SameSite=${options.sameSite || 'Strict'}`;
  cookieString += `; path=${options.path || '/'}`;
  
  if (options.expires) {
    cookieString += `; expires=${options.expires.toUTCString()}`;
  }
  
  if (options.domain) {
    cookieString += `; domain=${options.domain}`;
  }
  
  document.cookie = cookieString;
}

// Validate cookie data
function validateCookieData(data, schema) {
  if (typeof data !== 'string') {
    data = String(data);
  }
  
  // Sanitize data
  const sanitized = data
    .replace(/[<>]/g, '') // Remove potential HTML
    .substring(0, 4000); // Limit length
  
  // Validate against schema
  if (schema) {
    if (schema.maxLength && sanitized.length > schema.maxLength) {
      throw new Error('Cookie data too long');
    }
    
    if (schema.pattern && !schema.pattern.test(sanitized)) {
      throw new Error('Cookie data invalid format');
    }
  }
  
  return sanitized;
}

// Usage with validation
try {
  setSecureCookie('user_id', '123', {
    secure: true,
    sameSite: 'Strict'
  });
} catch (error) {
  console.error('Failed to set cookie:', error);
}

// Never store sensitive data in cookies
// BAD:
setCookie('password', 'user_password'); // NEVER DO THIS
setCookie('ssn', '123-45-6789'); // NEVER DO THIS
setCookie('api_key', 'secret_key_123'); // NEVER DO THIS

// GOOD:
setCookie('session_id', 'abc123', { secure: true, sameSite: 'Strict' });
setCookie('preferences', JSON.stringify({ theme: 'dark' }));

Cookie Management Class

Creating a comprehensive cookie management utility.

Cookie Manager Implementation

class CookieManager {
  constructor(options = {}) {
    this.defaults = {
      expires: null,
      maxAge: null,
      domain: null,
      path: '/',
      secure: window.location.protocol === 'https:',
      sameSite: 'Lax',
      ...options
    };
  }
  
  set(name, value, options = {}) {
    const config = { ...this.defaults, ...options };
    
    // Validate and sanitize
    if (typeof value !== 'string') {
      value = JSON.stringify(value);
    }
    
    const sanitizedValue = this.sanitize(value);
    const sanitizedName = this.sanitize(name);
    
    let cookieString = `${encodeURIComponent(sanitizedName)}=${encodeURIComponent(sanitizedValue)}`;
    
    if (config.expires) {
      cookieString += `; expires=${config.expires.toUTCString()}`;
    }
    
    if (config.maxAge) {
      cookieString += `; max-age=${config.maxAge}`;
    }
    
    if (config.domain) {
      cookieString += `; domain=${config.domain}`;
    }
    
    cookieString += `; path=${config.path}`;
    
    if (config.secure) {
      cookieString += '; secure';
    }
    
    cookieString += `; SameSite=${config.sameSite}`;
    
    document.cookie = cookieString;
    
    // Store options for deletion
    this._storeOptions(name, config);
    
    return true;
  }
  
  get(name, defaultValue = null) {
    const sanitizedName = this.sanitize(name);
    const nameEQ = sanitizedName + '=';
    const cookies = document.cookie.split(';');
    
    for (let i = 0; i < cookies.length; i++) {
      let cookie = cookies[i];
      
      while (cookie.charAt(0) === ' ') {
        cookie = cookie.substring(1, cookie.length);
      }
      
      if (cookie.indexOf(nameEQ) === 0) {
        const value = decodeURIComponent(cookie.substring(nameEQ.length, cookie.length));
        
        // Try to parse as JSON
        try {
          return JSON.parse(value);
        } catch {
          return value;
        }
      }
    }
    
    return defaultValue;
  }
  
  remove(name) {
    const sanitizedName = this.sanitize(name);
    const options = this._getStoredOptions(name) || this.defaults;
    
    let cookieString = `${encodeURIComponent(sanitizedName)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
    
    if (options.domain) {
      cookieString += `; domain=${options.domain}`;
    }
    
    cookieString += `; path=${options.path}`;
    
    if (options.secure) {
      cookieString += '; secure';
    }
    
    cookieString += `; SameSite=${options.sameSite}`;
    
    document.cookie = cookieString;
    
    this._removeStoredOptions(name);
  }
  
  getAll() {
    const cookies = {};
    
    if (document.cookie) {
      document.cookie.split(';').forEach(cookie => {
        const [name, value] = cookie.trim().split('=');
        if (name && value) {
          const decodedName = decodeURIComponent(name);
          const decodedValue = decodeURIComponent(value);
          
          try {
            cookies[decodedName] = JSON.parse(decodedValue);
          } catch {
            cookies[decodedName] = decodedValue;
          }
        }
      });
    }
    
    return cookies;
  }
  
  exists(name) {
    return this.get(name) !== null;
  }
  
  clear() {
    const cookies = this.getAll();
    Object.keys(cookies).forEach(name => this.remove(name));
  }
  
  // Private methods
  sanitize(value) {
    return String(value)
      .replace(/[;<>\\"']/g, '') // Remove dangerous characters
      .substring(0, 4000); // Limit length
  }
  
  _storeOptions(name, options) {
    if (!this._options) this._options = new Map();
    this._options.set(name, options);
  }
  
  _getStoredOptions(name) {
    return this._options ? this._options.get(name) : null;
  }
  
  _removeStoredOptions(name) {
    if (this._options) this._options.delete(name);
  }
}

// Usage
const cookies = new CookieManager({
  secure: true,
  sameSite: 'Strict'
});

// Set cookies
cookies.set('user', { id: 123, name: 'John' });
cookies.set('preferences', { theme: 'dark', language: 'en' }, {
  expires: new Date('2023-12-31')
});

// Get cookies
const user = cookies.get('user');
const theme = cookies.get('preferences', { theme: 'light' }).theme;

// Check existence
if (cookies.exists('session')) {
  console.log('Session exists');
}

// Remove specific cookie
cookies.remove('user');

// Get all cookies
const allCookies = cookies.getAll();
console.log('All cookies:', allCookies);

Practical Use Cases

Common scenarios where cookies are the appropriate solution.

Session Management

// Session management with cookies
class SessionManager {
  constructor() {
    this.cookieManager = new CookieManager({
      secure: true,
      sameSite: 'Strict',
      maxAge: 3600 // 1 hour
    });
  }
  
  login(userData) {
    const session = {
      id: this.generateSessionId(),
      userId: userData.id,
      loginTime: new Date().toISOString(),
      lastActivity: new Date().toISOString()
    };
    
    this.cookieManager.set('session', session);
    
    // Set user preferences cookie
    this.cookieManager.set('user_prefs', userData.preferences, {
      expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year
    });
    
    return session.id;
  }
  
  logout() {
    this.cookieManager.remove('session');
    // Optionally keep preferences
    // this.cookieManager.remove('user_prefs');
  }
  
  isLoggedIn() {
    const session = this.cookieManager.get('session');
    return session && this.isValidSession(session);
  }
  
  getCurrentUser() {
    const session = this.cookieManager.get('session');
    return session ? session.userId : null;
  }
  
  isValidSession(session) {
    if (!session) return false;
    
    const now = new Date();
    const lastActivity = new Date(session.lastActivity);
    const maxAge = 60 * 60 * 1000; // 1 hour
    
    return (now - lastActivity) < maxAge;
  }
  
  updateActivity() {
    const session = this.cookieManager.get('session');
    if (session) {
      session.lastActivity = new Date().toISOString();
      this.cookieManager.set('session', session);
    }
  }
  
  generateSessionId() {
    return 'sess_' + Math.random().toString(36).substr(2, 9);
  }
}

// Usage
const session = new SessionManager();

// Login
session.login({
  id: 123,
  name: 'John Doe',
  preferences: {
    theme: 'dark',
    language: 'en'
  }
});

// Check login status
if (session.isLoggedIn()) {
  console.log('User is logged in');
  console.log('Current user:', session.getCurrentUser());
}

// Update activity on user interaction
document.addEventListener('click', () => {
  session.updateActivity();
});

// Logout
session.logout();

User Preferences

// User preferences with cookies
class UserPreferences {
  constructor() {
    this.cookieManager = new CookieManager();
    this.defaults = {
      theme: 'light',
      language: 'en',
      fontSize: '16',
      autoSave: true,
      notifications: true
    };
  }
  
  get(key) {
    const prefs = this.cookieManager.get('preferences') || {};
    return prefs[key] !== undefined ? prefs[key] : this.defaults[key];
  }
  
  set(key, value) {
    let prefs = this.cookieManager.get('preferences') || {};
    prefs[key] = value;
    
    this.cookieManager.set('preferences', prefs, {
      expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year
    });
    
    this.applyPreference(key, value);
  }
  
  getAll() {
    return { ...this.defaults, ...this.cookieManager.get('preferences') };
  }
  
  reset() {
    this.cookieManager.set('preferences', this.defaults, {
      expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
    });
    this.applyAllPreferences();
  }
  
  applyPreference(key, value) {
    switch (key) {
      case 'theme':
        document.body.className = value;
        break;
      case 'language':
        document.documentElement.lang = value;
        break;
      case 'fontSize':
        document.body.style.fontSize = value + 'px';
        break;
    }
  }
  
  applyAllPreferences() {
    const prefs = this.getAll();
    Object.keys(prefs).forEach(key => {
      this.applyPreference(key, prefs[key]);
    });
  }
}

// Usage
const prefs = new UserPreferences();

// Set preferences
prefs.set('theme', 'dark');
prefs.set('language', 'es');
prefs.set('fontSize', '18');

// Get preferences
console.log('Current theme:', prefs.get('theme'));
console.log('All preferences:', prefs.getAll());

// Reset to defaults
prefs.reset();

Browser Compatibility and Limitations

Understanding cookie limitations across different browsers.

Cookie Limitations

// Check cookie limitations
function getCookieLimitations() {
  const test = 'test_cookie=' + 'x'.repeat(4000); // 4KB test
  document.cookie = test;
  
  const limitations = {
    sizeLimit: null,
    countLimit: null,
    supported: document.cookie !== undefined
  };
  
  // Test size limit
  if (document.cookie.includes(test)) {
    limitations.sizeLimit = '4KB+';
  } else {
    // Try smaller sizes
    for (let size = 4000; size >= 100; size -= 100) {
      const smallerTest = 'test_cookie=' + 'x'.repeat(size);
      document.cookie = smallerTest;
      if (document.cookie.includes(smallerTest)) {
        limitations.sizeLimit = `~${size} bytes`;
        break;
      }
    }
  }
  
  // Test count limit
  let cookieCount = 0;
  const testCookies = [];
  
  try {
    for (let i = 0; i < 100; i++) { // Test up to 100 cookies
      const name = `test_cookie_${i}`;
      document.cookie = `${name}=value`;
      if (document.cookie.includes(name)) {
        cookieCount++;
        testCookies.push(name);
      } else {
        break;
      }
    }
  } catch (e) {
    console.error('Error testing cookie count:', e);
  }
  
  // Clean up test cookies
  testCookies.forEach(name => {
    document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
  });
  
  limitations.countLimit = cookieCount;
  
  return limitations;
}

// Browser-specific considerations
function getBrowserInfo() {
  const info = {
    name: '',
    version: '',
    mobile: false,
    cookieSupport: true
  };
  
  // Detect browser
  const ua = navigator.userAgent;
  
  if (ua.includes('Chrome')) {
    info.name = 'Chrome';
  } else if (ua.includes('Firefox')) {
    info.name = 'Firefox';
  } else if (ua.includes('Safari') && !ua.includes('Chrome')) {
    info.name = 'Safari';
  } else if (ua.includes('Edge')) {
    info.name = 'Edge';
  }
  
  // Mobile detection
  info.mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
  
  // Cookie support in private browsing
  try {
    document.cookie = 'test=1';
    info.cookieSupport = document.cookie.includes('test=1');
    document.cookie = 'test=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
  } catch (e) {
    info.cookieSupport = false;
  }
  
  return info;
}

// Usage
const limitations = getCookieLimitations();
console.log('Cookie limitations:', limitations);

const browser = getBrowserInfo();
console.log('Browser info:', browser);

if (!browser.cookieSupport) {
  console.warn('Cookies are not supported (private browsing?)');
  // Fallback to sessionStorage or localStorage
}

Privacy Mode Detection

// Detect private browsing mode
function isPrivateMode() {
  // Safari
  const isSafari = navigator.userAgent.includes('Safari') && 
                   !navigator.userAgent.includes('Chrome');
  
  if (isSafari) {
    try {
      localStorage.setItem('test', '1');
      localStorage.removeItem('test');
      return false;
    } catch (e) {
      return true;
    }
  }
  
  // Chrome/Edge
  try {
    const test = 'test';
    window.indexedDB.open(test, 1).onupgraded = function(e) {
      if (e.oldVersion === null) {
        // DB didn't exist, likely private mode
        return true;
      }
    };
  } catch (e) {
    return true;
  }
  
  return false;
}

// Handle private mode
function handlePrivateMode() {
  if (isPrivateMode()) {
    console.warn('Private browsing mode detected');
    
    // Show message to user
    const message = document.createElement('div');
    message.textContent = 'Some features may not work in private browsing mode';
    message.style.cssText = `
      position: fixed;
      top: 20px;
      left: 50%;
      transform: translateX(-50%);
      background: #ffeb3b;
      color: #333;
      padding: 10px 20px;
      border-radius: 5px;
      z-index: 9999;
    `;
    document.body.appendChild(message);
    
    // Auto-remove after 5 seconds
    setTimeout(() => {
      if (message.parentNode) {
        message.parentNode.removeChild(message);
      }
    }, 5000);
    
    return true;
  }
  
  return false;
}

// Usage
if (handlePrivateMode()) {
  // Fallback behavior for private mode
  enableMemoryStorage();
}

Modern Alternatives

When to use localStorage, sessionStorage, or modern storage APIs instead of cookies.

Storage Comparison

// Storage API comparison
const storageComparison = {
  cookies: {
    capacity: '~4KB per cookie',
    expiration: 'Flexible',
    sentToServer: true,
    access: 'Client + Server',
    security: 'HttpOnly, Secure, SameSite',
    useCase: 'Session management, server data'
  },
  
  localStorage: {
    capacity: '~5-10MB',
    expiration: 'Manual',
    sentToServer: false,
    access: 'Client only',
    security: 'Same-origin policy',
    useCase: 'Client-side preferences, cache'
  },
  
  sessionStorage: {
    capacity: '~5-10MB',
    expiration: 'Session end',
    sentToServer: false,
    access: 'Client only',
    security: 'Same-origin policy',
    useCase: 'Temporary session data'
  }
};

// Choose appropriate storage
function chooseStorage(data, options = {}) {
  const { needsServerAccess = false, persistent = true, sensitive = false } = options;
  
  if (needsServerAccess) {
    return 'cookies';
  }
  
  if (sensitive) {
    return 'cookies'; // With proper security flags
  }
  
  if (persistent) {
    return 'localStorage';
  }
  
  return 'sessionStorage';
}

// Universal storage manager
class UniversalStorage {
  constructor() {
    this.cookies = new CookieManager();
    this.localStorage = window.localStorage;
    this.sessionStorage = window.sessionStorage;
  }
  
  set(key, value, options = {}) {
    const storageType = chooseStorage(value, options);
    
    switch (storageType) {
      case 'cookies':
        return this.cookies.set(key, value, options);
      case 'localStorage':
        return this.localStorage.setItem(key, JSON.stringify(value));
      case 'sessionStorage':
        return this.sessionStorage.setItem(key, JSON.stringify(value));
    }
  }
  
  get(key, defaultValue = null, options = {}) {
    const storageType = chooseStorage(defaultValue, options);
    let value;
    
    switch (storageType) {
      case 'cookies':
        value = this.cookies.get(key, defaultValue);
        break;
      case 'localStorage':
        value = JSON.parse(this.localStorage.getItem(key) || 'null');
        break;
      case 'sessionStorage':
        value = JSON.parse(this.sessionStorage.getItem(key) || 'null');
        break;
    }
    
    return value !== null ? value : defaultValue;
  }
  
  remove(key, options = {}) {
    const storageType = chooseStorage(null, options);
    
    switch (storageType) {
      case 'cookies':
        return this.cookies.remove(key);
      case 'localStorage':
        return this.localStorage.removeItem(key);
      case 'sessionStorage':
        return this.sessionStorage.removeItem(key);
    }
  }
}

// Usage
const storage = new UniversalStorage();

// Store user preferences (localStorage)
storage.set('preferences', { theme: 'dark' });

// Store session token (cookies)
storage.set('auth_token', 'abc123', {
  needsServerAccess: true,
  secure: true
});

// Store temporary data (sessionStorage)
storage.set('temp_form_data', { step: 2 }, {
  persistent: false
});

Modern Storage APIs

// IndexedDB for large structured data
class IndexedDBStorage {
  constructor(dbName, version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }
  
  async init() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);
      
      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };
      
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        
        // Create object store if it doesn't exist
        if (!db.objectStoreNames.contains('data')) {
          db.createObjectStore('data', { keyPath: 'id', autoIncrement: true });
        }
      };
    });
  }
  
  async set(key, value) {
    const transaction = this.db.transaction(['data'], 'readwrite');
    const store = transaction.objectStore('data');
    
    return new Promise((resolve, reject) => {
      const request = store.put({ id: key, value, timestamp: Date.now() });
      
      request.onsuccess = () => resolve(true);
      request.onerror = () => reject(request.error);
    });
  }
  
  async get(key) {
    const transaction = this.db.transaction(['data'], 'readonly');
    const store = transaction.objectStore('data');
    
    return new Promise((resolve, reject) => {
      const request = store.get(key);
      
      request.onsuccess = () => resolve(request.result ? request.result.value : null);
      request.onerror = () => reject(request.error);
    });
  }
  
  async remove(key) {
    const transaction = this.db.transaction(['data'], 'readwrite');
    const store = transaction.objectStore('data');
    
    return new Promise((resolve, reject) => {
      const request = store.delete(key);
      
      request.onsuccess = () => resolve(true);
      request.onerror = () => reject(request.error);
    });
  }
}

// Usage
const idbStorage = new IndexedDBStorage('MyAppDB');

// Initialize and use
idbStorage.init().then(() => {
  return idbStorage.set('user_data', { name: 'John', preferences: {} });
}).then(() => {
  return idbStorage.get('user_data');
}).then(data => {
  console.log('User data:', data);
});

Best Practices

Guidelines for using cookies effectively and securely.

Security Best Practices

// Always use secure flags for sensitive cookies
function setSecureCookie(name, value) {
  const options = {
    secure: window.location.protocol === 'https:',
    sameSite: 'Strict',
    httpOnly: false // Note: Can only be set server-side
  };
  
  // Set reasonable expiration
  if (name.includes('session') || name.includes('token')) {
    options.maxAge = 3600; // 1 hour
  } else {
    options.expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000); // 1 year
  }
  
  return new CookieManager().set(name, value, options);
}

// Validate and sanitize all cookie data
function sanitizeCookieValue(value) {
  if (typeof value !== 'string') {
    value = String(value);
  }
  
  // Remove potentially dangerous characters
  return value
    .replace(/[<>"'\\]/g, '') // Remove HTML/JS characters
    .substring(0, 4000); // Limit length
}

// Implement proper session management
class SecureSessionManager {
  constructor() {
    this.cookieManager = new CookieManager({
      secure: true,
      sameSite: 'Strict'
    });
  }
  
  createSession(userData) {
    const sessionId = this.generateSecureSessionId();
    const sessionData = {
      id: sessionId,
      userId: userData.id,
      createdAt: new Date().toISOString(),
      lastActivity: new Date().toISOString(),
      userAgent: navigator.userAgent.substring(0, 200) // Limit user agent
    };
    
    this.cookieManager.set('session', sessionData, {
      maxAge: 1800 // 30 minutes
    });
    
    return sessionId;
  }
  
  validateSession(session) {
    if (!session || !session.id) return false;
    
    const now = new Date();
    const createdAt = new Date(session.createdAt);
    const lastActivity = new Date(session.lastActivity);
    const maxAge = 30 * 60 * 1000; // 30 minutes
    
    // Check session age
    if (now - createdAt > maxAge) {
      return false;
    }
    
    // Check activity timeout
    if (now - lastActivity > maxAge) {
      return false;
    }
    
    return true;
  }
  
  generateSecureSessionId() {
    // Use cryptographically secure random generator if available
    if (window.crypto && window.crypto.getRandomValues) {
      const array = new Uint8Array(16);
      window.crypto.getRandomValues(array);
      return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
    }
    
    // Fallback
    return 'sess_' + Math.random().toString(36).substr(2, 16) + '_' + Date.now();
  }
}

Performance and Compatibility

// Minimize cookie operations
class OptimizedCookieManager {
  constructor() {
    this.cache = new Map();
    this.dirty = new Set();
  }
  
  get(name) {
    if (this.cache.has(name)) {
      return this.cache.get(name);
    }
    
    const value = this.getCookieFromDocument(name);
    this.cache.set(name, value);
    return value;
  }
  
  set(name, value, options = {}) {
    this.cache.set(name, value);
    this.dirty.add(name);
    
    // Batch write operations
    if (this.batchTimeout) {
      clearTimeout(this.batchTimeout);
    }
    
    this.batchTimeout = setTimeout(() => {
      this.flushDirty();
    }, 100);
  }
  
  flushDirty() {
    this.dirty.forEach(name => {
      const value = this.cache.get(name);
      this.setCookieInDocument(name, value);
    });
    
    this.dirty.clear();
  }
  
  // Browser compatibility checks
  isSupported() {
    return typeof document !== 'undefined' && 
           typeof document.cookie !== 'undefined';
  }
  
  getMaxCookieSize() {
    // Test actual cookie size limit
    let maxSize = 0;
    const testValue = 'x';
    
    while (maxSize < 5000) { // Reasonable upper limit
      const testCookie = `size_test=${testValue.repeat(maxSize)}`;
      document.cookie = testCookie;
      
      if (document.cookie.includes(testCookie)) {
        maxSize += 100;
      } else {
        break;
      }
      
      document.cookie = 'size_test=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
    }
    
    return maxSize;
  }
}

// Feature detection before usage
function initializeWithFallback() {
  if (typeof document !== 'undefined' && document.cookie) {
    // Use cookies
    return new CookieManager();
  } else if (typeof localStorage !== 'undefined') {
    // Fallback to localStorage
    console.warn('Cookies not available, using localStorage');
    return new LocalStorageManager();
  } else {
    // Final fallback to memory
    console.warn('No persistent storage available, using memory');
    return new MemoryStorageManager();
  }
}

// Usage
const storage = initializeWithFallback();
storage.set('preferences', { theme: 'dark' });

Summary

Key Takeaways

  • Cookies are sent to server with each HTTP request
  • Limited to ~4KB per cookie with domain-wide limits
  • Support expiration dates and security attributes
  • Essential for server-side session management
  • SameSite attribute provides CSRF protection

Best Practices

  • Use Secure flag for sensitive cookies over HTTPS
  • Use SameSite=Strict for CSRF protection
  • Always validate and sanitize cookie data
  • Use appropriate storage (cookies vs localStorage vs sessionStorage)
  • Implement proper session management with expiration

Common Pitfalls

  • Storing sensitive data in plaintext cookies
  • Not using security flags (Secure, HttpOnly, SameSite)
  • Exceeding cookie size limits
  • Not handling private browsing mode properly
  • Using cookies when localStorage would be more appropriate