Cookie, localStorage, sessionStorage
5 мест, где браузер хранит данные, и когда что использовать.
Обновлено
Зачем нужно хранилище в браузере
HTTP не хранит состояние: сервер не знает, что запрос 2 пришёл от того же человека, что и запрос 1. А страница при перезагрузке начинает с нуля. Чтобы что-то «запомнить» — вход, корзину, настройки, наполовину заполненную форму, — нужно место, где данные сохраняются.
Браузер даёт 5 таких мест, у каждого своя задача.
Сравнение видов хранилища
| Cookie | localStorage | sessionStorage | IndexedDB | Cache Storage | |
|---|---|---|---|---|---|
| Что хранит | небольшой текст | текст (ключ → строка) | текст (ключ → строка) | объекты, файлы, blob | целые HTTP-ответы |
| Объём | ~4 КБ на cookie | ~5 МБ на origin | ~5 МБ на origin | сотни МБ+ | сотни МБ+ |
| Автоматически уходит на сервер | да, с каждым запросом | нет | нет | нет | нет |
| Сколько живёт | до Max-Age / Expires |
всегда | пока жива вкладка | всегда | всегда |
| Видно в | всех вкладках | всех вкладках | только текущей вкладке | всех вкладках | всех вкладках |
| API | заголовок / document.cookie |
синхронный | синхронный | асинхронный | асинхронный |
| Читается из JS | да, если не HttpOnly |
да | да | да | да |
| Для чего | сессия, авторизация, анонимный id | настройки UI, черновики | состояние мастера / формы во вкладке | офлайн-данные, большой кэш | PWA, офлайн (service worker) |
Все они изолированы по origin: a.com не может прочитать то, что сохранил b.com.
Cookie
Cookie — это небольшой текст, который устанавливает сервер, а браузер автоматически отправляет обратно с каждым запросом к тому же сайту. Поэтому это единственный способ, которым сервер может вас узнать.
Set-Cookie: session=abc123; Max-Age=2592000; Path=/; HttpOnly; Secure; SameSite=Lax
| Атрибут | Что делает |
|---|---|
Max-Age / Expires |
сколько живёт; без них — сессионная cookie (исчезает при закрытии браузера) |
HttpOnly |
JS не может её прочитать → защита от XSS |
Secure |
отправляется только по HTTPS |
SameSite=Lax |
не отправляется с POST-запросами с других сайтов → защита от CSRF |
SameSite=Strict |
вообще не отправляется с других сайтов |
SameSite=None |
отправляется всегда (нужен Secure) — для межсайтовых интеграций |
Path / Domain |
для каких URL отправляется |
Это приложение ставит cookie vid (httpOnly) — так оно узнаёт ваш прогресс на сервере без регистрации.
В Next cookie читают через cookies() из next/headers, а устанавливают в Server Actions, Route Handlers или proxy.ts.
localStorage
Хранилище ключ → строка, постоянное, общее для всех вкладок одного сайта.
localStorage.setItem('theme', 'dark')
localStorage.getItem('theme') // 'dark'
localStorage.removeItem('theme')
localStorage.setItem('cart', JSON.stringify(cart)) // объекты → JSON
const cart = JSON.parse(localStorage.getItem('cart') ?? '[]')- Только строки — объекты проходят через
JSON.stringify/JSON.parse. - Синхронный API: блокирует поток; не сохраняйте мегабайты на каждое нажатие клавиши.
- Когда одна вкладка меняет
localStorage, остальные вкладки получают событиеstorage— так синхронизируют тему между вкладками.
sessionStorage
Тот же API, что и у localStorage, но данные живут только пока жива вкладка:
| Ситуация | sessionStorage |
|---|---|
| перезагрузка страницы (F5) | сохраняется |
| переход на другой URL в той же вкладке и обратно | сохраняется |
| открыть сайт в новой вкладке | пусто — у каждой вкладки своё хранилище |
| «Дублировать вкладку» | копируется в новую вкладку, дальше живёт отдельно |
| закрыть вкладку | удаляется |
sessionStorage.setItem('checkout-step', '2')
sessionStorage.getItem('checkout-step') // '2' — только в этой вкладкеКогда он лучше localStorage: мастер оформления заказа или длинная форма, открытые в двух вкладках, — каждая вкладка хранит свой прогресс и не мешает другой.
IndexedDB
База данных в браузере: таблицы (object stores), индексы, транзакции, целые объекты (не только строки), файлы. API асинхронный (не блокирует страницу), но многословный — на практике используют небольшую обёртку:
import { get, set } from 'idb-keyval'
await set('drafts', [{ id: 1, text: '...' }]) // объекты напрямую, без JSON
const drafts = await get('drafts')Для: офлайн-приложений, кэша больших данных, файлов пользователя до загрузки.
Cache Storage
Хранит целые пары запрос → ответ. Его используют service workers, чтобы сайт работал офлайн (PWA). В обычных приложениях вы редко трогаете его напрямую.
Правила безопасности
- Никогда не храните токены авторизации в
localStorage/sessionStorage— любой скрипт на странице (включая внедрённый через XSS) может их прочитать. Сессия живёт в cookie сHttpOnly. - Хранилище может выбросить ошибку (приватный режим, переполнение, заблокированные данные сайта) → оборачивайте каждое чтение / запись в
try/catch. - На сервере его нет: в Next читайте его только в Client Components после монтирования (в
useEffectилиuseSyncExternalStore), иначе будут ошибки гидратации. - Пользователь может очистить его в любой момент — это ненадёжное хранилище для важных данных.
Как выбрать
| Нужно... | Используйте |
|---|---|
| чтобы сервер знал, кто вы | cookie с HttpOnly |
| чтобы настройка пережила перезапуск | localStorage |
| отдельное состояние для каждой вкладки, исчезающее при закрытии | sessionStorage |
| большие данные, объекты, офлайн | IndexedDB |
| состояние, которым можно поделиться ссылкой (фильтры, страница) | URL, а не хранилище |
| важные данные на любом устройстве | сервер (база данных) |
Коротко
- Cookie — единственное, что уходит на сервер автоматически; для сессий
HttpOnly; Secure; SameSite=Lax. localStorage— постоянно, все вкладки;sessionStorage— на вкладку, до закрытия; оба только строки и синхронные.- IndexedDB — для больших и офлайн-данных; никаких секретов в хранилище; всегда
try/catch.