У браузера несколько мест, где сайт может хранить данные. Они отличаются объёмом, сроком жизни, тем, кто их видит, и тем, уходят ли они на сервер.
Cookie
Маленькие пары «имя=значение» (около 4 КБ на одну куку), которые браузер автоматически отправляет на сервер в заголовке Cookie с каждым подходящим запросом. Сервер ставит их заголовком Set-Cookie.
Set-Cookie: session=abc123; Path=/; Max-Age=86400; Secure; HttpOnly; SameSite=Lax
Атрибуты:
Expires/Max-Age- срок жизни. Без них кука сессионная и умрёт с закрытием браузера.Domain,Path- для каких адресов отправлять.Secure- только по HTTPS.HttpOnly- недоступна из JS (document.cookieеё не покажет). Защищает сессию от кражи через XSS.SameSite- отправлять ли куку при запросах с других сайтов:Strict,Lax,None(сNoneобязателенSecure). Подробно в модуле про безопасность.
Из JS: document.cookie читает строку всех доступных кук, а присваивание document.cookie = 'theme=dark; max-age=3600' добавляет или меняет одну куку, остальные не трогает.
localStorage и sessionStorage
Web Storage: синхронный API «ключ-строка», порядка 5 МБ на origin.
localStorage.setItem('theme', 'dark');
localStorage.getItem('theme'); // 'dark'
localStorage.setItem('user', JSON.stringify({ id: 1 })); // объекты только через JSON
localStorageживёт, пока его не очистят: переживает перезапуск браузера. Общий для всех вкладок одного origin.sessionStorageживёт, пока открыта вкладка, и у каждой вкладки свой. Переживает перезагрузку страницы. При дублировании вкладки копируется.
Оба привязаны к origin: протокол + домен + порт. http://site.ru и https://site.ru видят разные хранилища.
Событие storage приходит в другие вкладки того же origin, когда меняется localStorage. В вкладке, которая сделала запись, оно не срабатывает. Так синхронизируют, например, выход из аккаунта во всех вкладках.
Минусы: API синхронный и блокирует главный поток, хранит только строки, недоступен в воркерах, доступен любому скрипту на странице. Токены и секреты там держать опасно: при XSS их сразу украдут.
IndexedDB
Полноценная асинхронная база в браузере: хранилища объектов, индексы, транзакции. Хранит почти любые данные через structured clone: объекты, Date, Blob, ArrayBuffer. Объём намного больше, чем у Web Storage: это часть общей квоты, которая зависит от свободного места на диске. Доступна в воркерах.
API на событиях неудобен, поэтому обычно берут обёртку (например, idb или Dexie).
Cache API
caches хранит пары «запрос → ответ». Главный пользователь это Service Worker: он кладёт туда статику и отдаёт её офлайн. Доступен и из обычной страницы. Это не HTTP-кеш браузера: управляет им только ваш код.
Квоты и очистка
Браузер может удалить данные сайта при нехватке места. navigator.storage.estimate() показывает занято и доступно, navigator.storage.persist() просит не удалять данные автоматически. Safari может стереть хранилища, созданные скриптом, если пользователь не взаимодействовал с сайтом 7 дней.
Сводка
| Объём | Срок | На сервер | Из JS | |
|---|---|---|---|---|
| Cookie | ~4 КБ | по Max-Age или сессия | да, сам | если нет HttpOnly |
| localStorage | ~5 МБ | бессрочно | нет | да, синхронно |
| sessionStorage | ~5 МБ | вкладка | нет | да, синхронно |
| IndexedDB | большой | бессрочно | нет | да, асинхронно |
| Cache API | большой | бессрочно | нет | да, асинхронно |
Частые ошибки
- Хранить JWT в
localStorageи считать это безопасным. - Класть объект в
setItemбезJSON.stringifyи получать"[object Object]". - Ждать событие
storageв той же вкладке. - Думать, что
sessionStorageобщий для вкладок.
Что спросят на собесе
- Разница между cookie,
localStorageиsessionStorage. - Где хранить токен авторизации и почему.
- Что делают
HttpOnly,Secure,SameSite. - Когда нужен IndexedDB.
- Как синхронизировать состояние между вкладками.