Cookie, session, localStorage

HTTP - протокол без состояния. Каждый запрос - как новый звонок от незнакомца. Чтобы сервер помнил пользователя между запросами, нужно хранить состояние. Есть несколько способов - у каждого свои правила (на сервере сессии часто хранят в Redis, на PHP - через сессии).

Cookie

Cookie - маленькие текстовые данные, которые браузер хранит и автоматически отправляет серверу с каждым запросом к этому домену.

// Установка cookie (клиент)
document.cookie = "theme=dark; max-age=2592000; path=/";
// max-age=2592000 - 30 дней в секундах

// Чтение (клиент)
console.log(document.cookie); // "theme=dark; lang=ru"

Сервер устанавливает cookie через заголовок:

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400

Флаги cookie

HttpOnly - JavaScript не может читать (защита от XSS)
Secure - только через HTTPS
SameSite - контроль кросс-доменной отправки:
              Strict - не отправлять с других сайтов
              Lax - отправлять при навигации (GET), но не POST
              None - отправлять всегда (нужен Secure)
Max-Age - время жизни в секундах
Expires - конкретная дата истечения
Path - для какого пути действует cookie
Domain - для какого домена (включая поддомены)
Один cookie - максимум ~4KB. Всего на домен - ~50 cookie. Для больших данных используй localStorage или серверное хранилище.

Session (серверная сессия)

Сессия - данные пользователя, которые хранятся на сервере. В cookie хранится только идентификатор сессии (session ID).

Браузер                           Сервер
   │                                │
   │── Cookie: session_id=abc123 ──→│
   │                                │── Находит сессию abc123 в памяти/БД
   │                                │── Сессия: {userId: 42, role: "admin"}
   │←── Ответ с данными ───────────│

Преимущества: данные не видны клиенту, нельзя подделать (подделать можно только session ID). Недостатки: нагрузка на сервер, сложнее масштабировать (sticky sessions или shared storage).

localStorage

localStorage - хранилище ключ-значение в браузере. Данные сохраняются бессрочно (пока пользователь не очистит).

// Записать
localStorage.setItem('theme', 'dark');

// Прочитать
const theme = localStorage.getItem('theme'); // "dark"

// Удалить
localStorage.removeItem('theme');

// Очистить всё
localStorage.clear();

Хранение объектов

localStorage хранит только строки. Для объектов - JSON.stringify / JSON.parse:

// Записать объект
const user = { name: 'Иван', role: 'backend' };
localStorage.setItem('user', JSON.stringify(user));

// Прочитать объект
const saved = JSON.parse(localStorage.getItem('user'));
console.log(saved.name); // "Иван"
`getItem()` возвращает `null`, если ключа нет. `JSON.parse(null)` вернёт null, но `JSON.parse(undefined)` выбросит ошибку. Всегда проверяй.
// Безопасное чтение
function getFromStorage(key) {
  try {
    const value = localStorage.getItem(key);
    return value ? JSON.parse(value) : null;
  } catch {
    return null;
  }
}

sessionStorage

Как localStorage, но данные удаляются при закрытии вкладки.

sessionStorage.setItem('step', '3');
const step = sessionStorage.getItem('step'); // "3"
// Закрыл вкладку → данные пропали

Подходит для: мастер-форм (многошаговых), временных состояний, данных одной сессии.

Сравнение способов хранения

                  Cookie              localStorage        sessionStorage
Размер            ~4KB                ~5-10MB             ~5-10MB
Отправка серверу  Автоматически       Нет                 Нет
Срок жизни        Max-Age/Expires     Бессрочно           До закрытия вкладки
Доступ из JS      Да (без HttpOnly)   Да                  Да
Кросс-вкладки     Да                  Да (один домен)     Нет (изолировано)

Что где хранить

✅ Cookie (HttpOnly, Secure):
 - Session ID
 - Токены аутентификации (если не JWT)
 - Настройки, нужные серверу (язык, A/B тест)

✅ localStorage:
 - Тема (dark/light)
 - Язык интерфейса
 - Кешированные данные (прогресс курса)
 - Настройки UI (скрытые подсказки)

✅ sessionStorage:
 - Данные многошаговых форм
 - Временное состояние (позиция скролла)

❌ Никогда не храни в браузере:
 - Пароли
 - Приватные ключи
 - Персональные данные (паспорт, карта)

JWT в localStorage vs HttpOnly cookie

JWT в localStorage:
  + Простой доступ из JS
  + Удобно для SPA
 - Уязвим к XSS (скрипт может украсть токен)

JWT в HttpOnly cookie:
  + JS не может прочитать (защита от XSS)
  + Автоматически отправляется серверу
 - Нужна защита от CSRF (SameSite помогает)
 - Сложнее для кросс-доменных API
Если не уверен - храни токен в HttpOnly cookie с SameSite=Lax. Это защищает от XSS и большинства CSRF-атак. localStorage для токенов - только если понимаешь риски.

Практический пример: тема сайта

// Загрузить тему при старте
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);

// Переключить тему
function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
}
[data-theme="dark"] {
 --color-bg: #1a202c;
 --color-text: #e2e8f0;
}

[data-theme="light"] {
 --color-bg: #ffffff;
 --color-text: #1a202c;
}

Мини-задание

  • Сохрани тему в localStorage и загружай при открытии страницы
  • Посмотри cookies в DevTools → Application → Cookies (найди HttpOnly и Secure)
  • Сделай счётчик посещений через localStorage (visits++)
  • Сохрани объект в localStorage через JSON.stringify и прочитай через JSON.parse
  • Сравни: открой два окна с одним сайтом - localStorage общий, sessionStorage раздельный

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.