Cookie: как работают

Cookie — небольшие данные, которые сервер отправляет браузеру в заголовке Set-Cookie, и браузер автоматически возвращает их при каждом следующем запросе к тому же домену.

Зачем нужно

HTTP — stateless протокол: каждый запрос независим. Куки — основной механизм сохранения состояния: идентификатор сессии, пользовательские настройки, корзина. Понимание механизма работы куки необходимо для реализации аутентификации, отладки и настройки безопасности.

Где используется

  • Session ID — идентификатор серверной сессии
  • Refresh Token — долгоживущий токен для обновления JWT
  • Пользовательские настройки (язык, тема) без авторизации
  • Аналитика (GA, Yandex Metrika) — трекинг пользователей

Жизненный цикл куки

1. Сервер устанавливает куку:
   HTTP/1.1 200 OK
   Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Max-Age=86400

2. Браузер сохраняет куку

3. При каждом запросе к домену — браузер добавляет заголовок:
   GET /api/profile HTTP/1.1
   Cookie: sessionId=abc123

4. Сервер читает куку из req.cookies.sessionId

Атрибуты куки

Set-Cookie: name=value; Path=/; Domain=example.com; Max-Age=86400; Expires=Wed, 09 Apr 2026 00:00:00 GMT; HttpOnly; Secure; SameSite=Lax
Атрибут Описание
Path=/ URL-путь, для которого действует кука
Domain=example.com Домен (включает поддомены)
Max-Age=86400 Время жизни в секундах
Expires=... Дата истечения (UTC)
HttpOnly Недоступна через document.cookie
Secure Только по HTTPS
SameSite=Lax/Strict/None Контроль кросс-сайт отправки

Работа с куками

Сервер (Express)

const cookieParser = require('cookie-parser');
app.use(cookieParser(process.env.COOKIE_SECRET));

// Установка
app.get('/login', (req, res) => {
  res.cookie('sessionId', 'abc123', {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 24 * 60 * 60 * 1000, // 24 часа в мс
    path: '/',
  });
  res.json({ ok: true });
});

// Чтение
app.get('/profile', (req, res) => {
  const sessionId = req.cookies.sessionId;
  // Подписанная кука (защита от подделки)
  const signed = req.signedCookies.sessionId;
});

// Удаление
app.post('/logout', (req, res) => {
  res.clearCookie('sessionId', { path: '/' });
  res.json({ ok: true });
});

Браузер (JavaScript — только не-HttpOnly куки)

// Чтение всех доступных куки
const allCookies = document.cookie; // "theme=dark; lang=ru"

// Установка (не HttpOnly, нет срока — сессионная)
document.cookie = 'theme=dark; path=/';

// С атрибутами
document.cookie = 'lang=ru; path=/; max-age=31536000; SameSite=Lax';

// Удаление (устанавливаем с истёкшей датой)
document.cookie = 'theme=; path=/; max-age=0';

// Парсинг куки в объект
const cookies = Object.fromEntries(
  document.cookie.split('; ').map(c => c.split('='))
);

Чтение куки из заголовка ответа

// Из ответа fetch заголовок Set-Cookie недоступен в браузере
// (защита браузера от JS-доступа к куке)
// Куки устанавливаются браузером автоматически
const res = await fetch('/api/login', { credentials: 'include' });
// Сервер установил Set-Cookie — браузер сохранил автоматически

Частые ошибки

  • Хранение секретов (паролей, токенов) без HttpOnly — XSS их похищает
  • Не указывают Path=/ — кука работает только с текущего URL
  • Устанавливают Domain=.example.com — кука доступна всем поддоменам (опасно)
  • Путают Max-Age (секунды) и maxAge в Express (миллисекунды)

Связанные темы

Ресурсы