Перейти к основному содержимому

Хранение данных в браузере

Разработчику Архитектору

Хранение в браузере - cookies, storage и IndexedDB

Хранение данных в браузере — это совокупность механизмов и технологий, предоставляемых веб-платформой, которые позволяют веб-приложениям сохранять информацию непосредственно на устройстве пользователя, обеспечивая постоянное или сессионное хранение данных между загрузками страниц и даже после закрытия браузера, что критически важно для создания удобного, персонализированного и работающего в офлайн-режиме пользовательского опыта. Браузерное хранение включает в себя несколько основных подходов, каждый из которых имеет свои характеристики, ограничения и сценарии использования, начиная от простых текстовых пар ключ-значение в localStorage и sessionStorage, через куки для небольших объемов данных, передаваемых на сервер, и заканчивая мощными базами данных IndexedDB для хранения структурированных и бинарных данных большого объема. Выбор конкретного механизма хранения зависит от множества факторов, включая требуемый объем данных, необходимость синхронизации с сервером, требования безопасности, поддержку офлайн-режима, производительность и простоту использования, причем современные приложения часто комбинируют несколько подходов для достижения оптимальных результатов. Важной особенностью всех механизмов хранения в браузере является их привязка к источнику, то есть к комбинации протокола, домена и порта, что гарантирует, что данные одного веб-приложения не могут быть прочитаны или модифицированы другим, обеспечивая изоляцию и безопасность на уровне платформы. С развитием веб-технологий и появлением прогрессивных веб-приложений хранение данных в браузере становится все более мощным и гибким, позволяя создавать приложения, которые работают в условиях нестабильного интернет-соединения, сохраняют состояние между сессиями и предоставляют пользователям быстрый доступ к их данным без необходимости постоянного обращения к серверу. Понимание всех доступных механизмов хранения и их особенностей является обязательным навыком для современного веб-разработчика, позволяющим создавать эффективные, отзывчивые и пользовательски-ориентированные приложения, соответствующие ожиданиям современных пользователей.

Страница может сохранять данные на клиенте — настройки темы, черновик формы, кэш справочника, идентификатор сессии. У каждого API свой объём, срок жизни и модель безопасности. Выбор неправильного хранилища ведёт к утечкам, переполнению квоты или потере данных при закрытии вкладки.

Краткие упоминания есть в основах JavaScript; здесь — сравнение и практика.


Распределение данных по хранилищам

В одном приложении обычно сосуществуют несколько типов данных — тема UI, сессия, черновик формы, офлайн-кэш. Один механизм "на всё" (часто localStorage) смешивает секреты, настройки и большие объёмы. Разделяйте по назначению: вкладка → sessionStorage; сессия с сервером → HttpOnly cookie; настройки → localStorage; крупные структуры → IndexedDB (см. сводную таблицу ниже).


Сводная таблица

МеханизмОбъём (ориентир)Срок жизниОтправка на серверТип данных
Cookie~4 КБ на cookieзадаётся Expires / Max-Ageда, с каждым запросом к доменустрока
sessionStorage~5 МБ на originдо закрытия вкладкинетстрока (ключ–значение)
localStorage~5 МБ на originпока пользователь не очиститнетстрока
IndexedDBсотни МБ+ (браузер решает)постоянно, пока не удалятнетобъекты, бинарные данные

Все четыре привязаны к origin (схема + хост + порт). Поддомены — отдельные origin, если не настроено иначе.


sessionStorage и localStorage

sessionStorage — это механизм веб-хранения, который предоставляет веб-приложениям возможность сохранять данные в виде пар ключ-значение на протяжении всей текущей сессии браузера в рамках конкретной вкладки, при этом данные автоматически очищаются, когда пользователь закрывает вкладку или окно, что делает этот механизм идеальным для хранения временной информации, актуальной только для текущего сеанса работы пользователя. Объект sessionStorage доступен через глобальное свойство window.sessionStorage и предоставляет простой синхронный API с методами setItem для сохранения данных, getItem для их получения, removeItem для удаления конкретного ключа и clear для полной очистки всего хранилища, причем все значения хранятся в виде строк, поэтому перед сохранением сложных данных, таких как объекты или массивы, необходимо преобразовывать их в JSON-строку с помощью JSON.stringify, а при извлечении парсить обратно с помощью JSON.parse. Основное преимущество sessionStorage заключается в его простоте использования и автоматическом управлении временем жизни данных, что освобождает разработчика от необходимости вручную очищать хранилище, а также в его изолированности, поскольку данные, сохраненные в одной вкладке, недоступны из других вкладок или окон браузера, даже если они открыты на том же сайте, что обеспечивает надежную изоляцию данных между разными пользовательскими сессиями. Емкость sessionStorage обычно ограничена примерно 5-10 мегабайтами на источник, что достаточно для хранения небольших объемов данных, таких как состояние форм, временные настройки интерфейса, данные корзины покупок на время сессии, результаты промежуточных вычислений или информация о действиях пользователя на текущей странице. Важной особенностью sessionStorage является его синхронный характер, что означает, что все операции чтения и записи выполняются немедленно и блокируют основной поток выполнения, поэтому при работе с большими объемами данных или в критических по производительности участках кода следует быть осторожным и использовать более специализированные механизмы хранения, такие как IndexedDB. sessionStorage широко используется для хранения временных данных, которые не должны сохраняться между визитами пользователя или передаваться на сервер, и является незаменимым инструментом для создания плавного и персонализированного пользовательского опыта в рамках одной сессии.

localStorage — это механизм веб-хранения, который предоставляет веб-приложениям возможность сохранять данные в виде пар ключ-значение на неопределенный срок, при этом данные сохраняются даже после закрытия браузера и перезагрузки компьютера и остаются доступными до тех пор, пока они не будут явно удалены пользователем или самим приложением, что делает этот механизм идеальным для постоянного хранения пользовательских настроек, предпочтений и других данных, которые должны сохраняться между визитами. Объект localStorage доступен через глобальное свойство window.localStorage и предоставляет тот же простой синхронный API, что и sessionStorage, включая setItem, getItem, removeItem и clear, причем все значения также хранятся в виде строк, и данные привязаны к конкретному источнику, что гарантирует, что они доступны только для того же домена, протокола и порта, которые их создали. Емкость localStorage также ограничена примерно 5-10 мегабайтами на источник, что достаточно для хранения разнообразных пользовательских данных, таких как настройки темы, язык интерфейса, сохраненные пароли или токены, состояние корзины покупок между сессиями, закешированные данные, не требующие синхронизации с сервером, и другие постоянные предпочтения пользователя. Одним из ключевых преимуществ localStorage является его постоянство и доступность независимо от состояния сети, что позволяет приложениям загружаться мгновенно с сохраненными настройками даже в офлайн-режиме, а также его простота и синхронность, что делает его удобным для небольших объемов данных, не требующих сложных запросов или индексации. Однако, как и sessionStorage, localStorage является синхронным и блокирующим, что может негативно сказаться на производительности при работе с большими объемами данных, а также он не поддерживает структурированные данные, бинарные объекты или сложные запросы, что ограничивает его применение в более сложных сценариях. localStorage следует использовать для хранения некритичных данных, не содержащих конфиденциальной информации, поскольку данные хранятся в открытом виде и доступны через консоль разработчика, и при работе с чувствительными данными необходимо использовать дополнительные меры шифрования или другие механизмы хранения, такие как куки с флагом HttpOnly или сессионные токены.

API одинаковый; разница только в времени жизни.

localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme'); // 'dark'
localStorage.removeItem('theme');
localStorage.clear(); // всё для этого origin

sessionStorage.setItem('wizard-step', '2');

Разбор:

  • setItem(key, value) сохраняет строковое значение по ключу в выбранном хранилище.
  • getItem('theme') возвращает строку или null, если ключ отсутствует.
  • removeItem('theme') удаляет один конкретный ключ без влияния на остальные данные.
  • clear() очищает всё хранилище текущего origin, поэтому его используют осторожно.
  • sessionStorage изолирован рамками текущей вкладки, в отличие от localStorage.

Хранятся только строки. Объекты — через JSON:

const draft = { title: 'Черновик', body: '…' };
localStorage.setItem('post-draft', JSON.stringify(draft));

const saved = JSON.parse(localStorage.getItem('post-draft') ?? 'null');

Разбор:

  • JSON.stringify(draft) сериализует объект в строку, пригодную для хранения в Web Storage.
  • JSON.parse(...) восстанавливает структуру объекта при чтении из storage.
  • Оператор ?? 'null' подставляет безопасное значение, если ключа нет, и предотвращает ошибку парсинга.
  • Такой цикл "serialize/deserialize" нужен для любых сложных структур — массивов, вложенных объектов, настроек.
  • В продакшене полезно добавлять версию схемы, чтобы корректно мигрировать старые сохранённые данные.

Когда использовать

  • localStorage — тема, язык UI, флаги "подсказку уже закрывали", несекретные настройки.
  • sessionStorage — многошаговая форма в одной вкладке, временное состояние мастера.

Ограничения

  • Синхронный API — на больших данных блокирует главный поток.
  • Нет доступа из Web Worker напрямую (только через main thread или обёртки).
  • При приватном режиме данные могут не сохраняться между сессиями.

Cookies

Cookies — это небольшие фрагменты текстовых данных, которые веб-сервер отправляет веб-браузеру, а браузер сохраняет их на компьютере пользователя и возвращает серверу при каждом последующем запросе к этому серверу, позволяя веб-сайтам запоминать состояние и информацию о пользователе между сессиями, обеспечивая такие функции, как аутентификация, персонализация, хранение предпочтений и отслеживание поведения пользователя. В отличие от localStorage и sessionStorage, которые доступны только на клиенте, куки автоматически отправляются на сервер с каждым HTTP-запросом, что делает их основным механизмом для поддержки сессий и аутентификации, позволяя серверу идентифицировать пользователя и восстанавливать его состояние даже при отсутствии других механизмов хранения. Куки имеют важные атрибуты для управления их поведением и безопасностью, включая expires или max-age для установки времени жизни, path для ограничения области действия, domain для указания домена, secure для отправки только по протоколу HTTPS, HttpOnly для запрета доступа через JavaScript, что защищает от XSS-атак, и SameSite для контроля отправки при межсайтовых запросах, что защищает от CSRF-атак. Основные ограничения кук включают их небольшой размер, обычно не более 4 килобайт на куку, ограниченное количество кук на домен, обычно около 50, и их отправку на сервер с каждым запросом, что может создавать дополнительный сетевой трафик и замедлять работу приложения, особенно при большом количестве или размере кук. В современной веб-разработке куки используются в основном для управления сессиями и хранения маркеров аутентификации, в то время как для хранения пользовательских настроек и других клиентских данных предпочтение отдается localStorage или IndexedDB, но куки остаются критически важным инструментом для работы с серверной стороной и обеспечения безопасности. Куки также являются объектом строгих законодательных требований, таких как GDPR, которые обязывают разработчиков получать информированное согласие пользователя на использование кук, особенно для целей отслеживания и персонализированной рекламы, что делает управление куками важной частью юридического соответствия веб-приложений.

Устанавливаются заголовком Set-Cookie с сервера или из JS (с ограничениями):

document.cookie = 'prefs=compact; path=/; max-age=31536000; SameSite=Lax';

Разбор:

  • document.cookie = ... создаёт или обновляет cookie в формате имя=значение; атрибуты.
  • path=/ делает cookie доступной по всему сайту, а не только в текущем разделе URL.
  • max-age=31536000 задаёт срок жизни в секундах (примерно один год).
  • SameSite=Lax снижает риск CSRF при кросс-сайтовых переходах, сохраняя типовой UX.
  • JavaScript не может выставить HttpOnly, поэтому чувствительные сессионные cookie лучше ставить с сервера.

Чтение — разбор строки document.cookie (неудобно); для сложной логики чаще работают через сервер или библиотеку.

АтрибутНазначение
HttpOnlyнедоступно из JS — защита от кражи через XSS
Secureтолько по HTTPS
SameSiteограничение при кросс-сайтовых запросах
Path / Domainобласть действия

Сессия и JWT: токены аутентификации с флагом HttpOnly не кладут в localStorage — при XSS их прочитают. Предпочтительно cookie с HttpOnly + Secure или память вкладки + refresh по короткоживущей cookie.


Logout и очистка данных сайта

Logout — это процесс выхода пользователя из системы в веб-приложении, который заключается в завершении его аутентифицированной сессии, очистке всех данных аутентификации как на стороне клиента, так и на стороне сервера, и возвращении пользователя в состояние неавторизованного доступа, при этом обычно требуется выполнение нескольких связанных действий для обеспечения полной и безопасной деавторизации. На стороне клиента logout включает в себя удаление или инвалидацию всех данных, связанных с сессией пользователя, включая удаление токенов доступа и обновления из localStorage, sessionStorage или кук, очистку состояния приложения, связанного с аутентифицированным пользователем, и перенаправление пользователя на страницу входа или общедоступную страницу, причем важно убедиться, что все очищенные данные действительно удалены и не могут быть восстановлены или использованы злоумышленником. На стороне сервера logout обычно включает в себя инвалидацию сессионного идентификатора на сервере, удаление сессионных записей из базы данных или кеша, добавление токена в черный список для предотвращения его дальнейшего использования в случае, если он был скомпрометирован, и отправку соответствующего ответа клиенту, подтверждающего успешный выход из системы. Важной частью logout является обеспечение безопасности, особенно при работе с одноразовыми токенами и куками с флагом HttpOnly, когда клиентский JavaScript не имеет прямого доступа к данным аутентификации, и для logout требуется выполнение специального запроса к серверу, который очищает сессию на серверной стороне и возвращает инструкции для очистки клиентских данных. Также необходимо учитывать сценарий множественных вкладок или устройств, где logout в одной вкладке должен приводить к выходу из системы во всех активных сессиях, что может быть реализовано через использование механизмов межтабличной коммуникации, таких как событие storage, или через регулярную проверку статуса аутентификации на сервере. Процесс logout должен быть тщательно спроектирован и протестирован, поскольку ошибки в его реализации могут привести к тому, что пользователь останется частично аутентифицированным, создавая уязвимости для атак, или к потере пользовательских данных, если они не были правильно сохранены перед выходом, что делает logout критической операцией с точки зрения безопасности и пользовательского опыта.

Для сценария logout сервер может вернуть заголовок Clear-Site-Data, чтобы сразу удалить клиентские данные текущего origin.

Clear-Site-Data: "cache", "cookies", "storage"

Разбор:

  • Заголовок Clear-Site-Data отправляет сервер и управляет очисткой клиентских данных для текущего origin.

  • Токен "cache" удаляет HTTP-кэш, "cookies" очищает cookie, "storage" сбрасывает web storage и IndexedDB.

  • Это системный механизм браузера, который удобен для logout и аварийной очистки состояния клиента.

  • Заголовок применяют вместе с серверной инвалидиацией сессии, чтобы убрать и токены, и локальные артефакты.

  • "cache" очищает HTTP-кэш;

  • "cookies" удаляет cookie;

  • "storage" очищает localStorage, sessionStorage, IndexedDB и Cache API;

  • "*" очищает всё сразу.

Практика — на logout сначала инвалидируйте сессию на сервере, затем отдайте Clear-Site-Data, и после этого делайте редирект на страницу входа.

Подробный разбор синтаксиса и ограничений — в справочнике по HTTP.


IndexedDB

IndexedDB — это мощная встроенная база данных в браузере, которая предоставляет веб-приложениям возможность хранить большие объемы структурированных данных, включая файлы, изображения, видео и другие бинарные объекты, с поддержкой сложных запросов, индексов, транзакций и асинхронного доступа, что делает ее полноценной альтернативой серверным базам данных для клиентских приложений, работающих в офлайн-режиме. IndexedDB является объектно-ориентированной базой данных, где данные хранятся в виде объектов, каждый из которых имеет уникальный ключ, а также может иметь индексы для быстрого поиска по другим свойствам, при этом поддерживается несколько типов данных, включая числа, строки, даты, булевы значения, массивы и объекты, а также бинарные данные через Blob и ArrayBuffer. Одним из ключевых преимуществ IndexedDB является ее огромная емкость, которая обычно ограничивается только доступным дисковым пространством устройства, что позволяет хранить гигабайты данных, и ее асинхронный характер, который гарантирует, что операции с базой данных не блокируют основной поток выполнения, обеспечивая отзывчивость интерфейса даже при работе с большими объемами данных. IndexedDB работает с использованием асинхронного API на основе промисов и событий, поддерживает транзакции для обеспечения целостности данных при выполнении нескольких операций одновременно, и предоставляет мощные возможности для фильтрации, сортировки и агрегации данных с помощью курсоров и индексов, что позволяет выполнять сложные запросы, сравнимые с запросами в реляционных базах данных. Основные сценарии использования IndexedDB включают хранение пользовательских данных для офлайн-режима в прогрессивных веб-приложениях, кеширование результатов сетевых запросов для ускорения работы, хранение черновиков и временных данных, создание локальных копий данных для работы без интернета, и обработку больших файлов, таких как изображения, аудио и видео, которые могут быть загружены и сохранены локально для быстрого доступа. Несмотря на свою мощь, IndexedDB имеет более сложный API по сравнению с localStorage или sessionStorage, что требует от разработчика большего понимания и усилий для интеграции, однако существуют высокоуровневые библиотеки и обертки, такие как Dexie.js и idb, которые значительно упрощают работу с IndexedDB, предоставляя более декларативный и удобочитаемый интерфейс для выполнения стандартных операций.

Клиентская база объектов с индексами и транзакциями. Подходит для офлайн-кэша, больших списков, файловых blob.

Код ITЗагрузка примера кода…

Разбор:

  • indexedDB.open('AppCache', 1) открывает базу и использует номер версии для миграций схемы.
  • Обработчик onupgradeneeded выполняется при создании базы или повышении версии и подходит для createObjectStore.
  • createObjectStore('articles', { keyPath: 'id' }) задаёт хранилище объектов с первичным ключом id.
  • onsuccess возвращает объект IDBDatabase, а onerror передаёт причину ошибки открытия.
  • В saveArticle транзакция readwrite открывает запись, put(article) создаёт или обновляет объект.
  • Завершение через tx.oncomplete гарантирует, что изменения действительно зафиксированы.

API событийное и многословное; в проектах часто оборачивают в idb или хранят в Service Worker + Cache API для статики.


Когда IndexedDB

  • офлайн-first приложение (PWA);
  • тысячи записей с поиском по полю;
  • кэш ответов API с версионированием схемы (version в open).

Не нужен для пары строк настроек — хватит localStorage.

PWA — это аббревиатура от Progressive Web App, что означает прогрессивное веб-приложение, и представляет собой современный подход к созданию веб-приложений, которые используют новейшие веб-технологии для обеспечения пользовательского опыта, сравнимого с нативными мобильными приложениями, включая работу в офлайн-режиме, быструю загрузку, установку на домашний экран устройства, отправку push-уведомлений и доступ к аппаратным возможностям устройства. Ключевыми технологическими компонентами PWA являются Service Worker, который работает как прокси-сервер между приложением и сетью, позволяя кешировать ресурсы и обеспечивать офлайн-доступность, и файл манифеста, который представляет собой JSON-документ, определяющий внешний вид и поведение приложения при установке на устройство, включая иконки, стартовый URL, цвета, ориентацию экрана и режим отображения. PWA используют механизмы браузерного хранения данных, включая localStorage, IndexedDB и кеши, для сохранения пользовательских данных, состояния приложения и ресурсов, что позволяет приложению работать и предоставлять доступ к данным даже при отсутствии интернет-соединения, синхронизируясь с сервером, когда соединение восстанавливается. Основные преимущества PWA включают единую кодовую базу для всех платформ, отсутствие необходимости публикации в магазинах приложений, автоматические обновления без участия пользователя, сниженные затраты на разработку и поддержку по сравнению с нативными приложениями, и повышенную доступность, поскольку приложения доступны через URL и не требуют установки для начального использования. PWA также поддерживают важные функции для вовлечения пользователей, включая отправку push-уведомлений через Service Worker даже когда приложение закрыто, фоновую синхронизацию для обновления данных в офлайн-режиме, и возможность добавления ярлыка на домашний экран, который открывает приложение в полноэкранном режиме без адресной строки браузера, создавая впечатление нативного приложения. С развитием веб-платформы PWA становятся все более мощными и широко распространенными, поддерживаемыми всеми основными браузерами и операционными системами, и представляют собой будущее веб-разработки, позволяя создавать высококачественные, доступные и производительные приложения, которые преодолевают разрыв между вебом и нативными платформами.


Безопасность

РискМера
XSS читает localStorageне хранить секреты; Content-Security-Policy, санитизация вывода
CSRF с cookieSameSite, токены в заголовке, проверка origin на сервере
Утечка между вкладкамиsessionStorage изолирует вкладку; cookie — общие для браузера
Потеря данныхкритичное — на сервере; клиент — только кэш

Выбор за 30 секунд

  1. Нужно отправлять серверу каждый запрос? → cookie (часто с сервера).
  2. Настройки UI, некритично, навсегда? → localStorage.
  3. Только пока открыта вкладка? → sessionStorage.
  4. Много записей, офлайн, индексы? → IndexedDB.

Практический шаблон — что где хранить в одном приложении

Пример для типичного кабинета пользователя:

ДанныеРекомендуемое хранилищеПочему
Тема интерфейса, плотность таблицlocalStorageбыстрый доступ, мало данных
Текущий шаг мастера "Оформление заказа"sessionStorageизоляция по вкладкам
Сессионный идентификаторHttpOnly cookieзащита от чтения через XSS
Каталог товаров для офлайн-поискаIndexedDBмного записей, индексы и транзакции

Эта схема хорошо стыкуется с валидацией форм, чтением файлов и работой с Service Worker, когда проект переходит к офлайн-режиму.


Антипаттерны

  • Секреты и refresh-токены складываются в localStorage, что повышает риск утечки при XSS.
  • Большие JSON-блоки записываются в localStorage на каждом символе ввода и вызывают "фризы".
  • Cookie используется как универсальное хранилище интерфейсных флагов, из-за чего растёт размер каждого HTTP-запроса.
  • Нет политики очистки старых данных и миграции схемы IndexedDB между версиями приложения.

Краткий итог

Четыре уровня хранения дополняют друг друга. Для веб-приложений по умолчанию — настройкиlocalStorage, сессия авторизацииHttpOnly cookie, офлайн-данные — IndexedDB. Всё чувствительное дублируйте проверкой на сервере.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.