Cookies и localStorage: как хранить данные в браузере правильно
Для кого эта статья:
- начинающие веб-разработчики
- профессиональные разработчики, желающие улучшить свои навыки
студенты и обучающиеся, заинтересованные в веб-разработке
Хранение данных на стороне клиента превращает обычные сайты в динамические, персонализированные платформы. Без cookies и localStorage пользователям пришлось бы заново авторизоваться при каждом клике, настройки интерфейса сбрасывались бы после обновления страницы, а корзины покупок пустели сами собой. Овладение этими технологиями — тот фундаментальный навык, который отличает профессионального разработчика от дилетанта. Готовы погрузиться в мир клиентского хранилища данных и выйти оттуда экспертом? 🚀
Хотите не просто прочитать об управлении данными в браузере, а научиться применять эти знания в реальных проектах? Курс Обучение веб-разработке от Skypro погружает вас в практику с первых занятий. Вы создадите полноценные веб-приложения с персонализацией на основе cookies и localStorage под руководством действующих разработчиков, а не теоретиков. Ваше портфолио пополнится проектами, которые впечатлят работодателей еще до окончания обучения.
Основы работы с cookies и localStorage в веб-разработке
Клиентское хранилище данных — неотъемлемая часть современной веб-разработки. Представьте, что пользователь заходит на ваш сайт, и вы хотите запомнить его предпочтения или состояние приложения. Именно тут вступают в игру cookies и localStorage. 🍪
Cookies — это небольшие текстовые файлы, которые веб-сервер отправляет браузеру пользователя. Они хранятся локально и отправляются обратно на сервер с каждым HTTP-запросом. Cookies существуют с 1994 года и изначально создавались для отслеживания состояния корзины в интернет-магазинах.
localStorage — часть Web Storage API, представленного в HTML5. Это механизм, позволяющий браузерам хранить пары ключ-значение, связанные с конкретным доменом. В отличие от cookies, данные localStorage не отправляются на сервер с каждым запросом.
Алексей Кузнецов, Lead Front-end Developer
Однажды наша команда работала над обновлением интерфейса административной панели для крупного новостного портала. Клиент жаловался на необходимость постоянно настраивать фильтры и сортировку после перезагрузки страницы. Самым очевидным решением казалось сохранение параметров на сервере, но это требовало дополнительных запросов и усложняло архитектуру.
Мы реализовали гибридный подход: основные настройки интерфейса сохраняли в localStorage (цветовая схема, размер шрифта, расположение панелей), а критичные фильтры данных — в cookies с передачей на сервер. Это позволило сократить время загрузки страницы на 40%, так как сервер сразу возвращал отфильтрованные данные, а клиент мгновенно применял сохранённые настройки интерфейса. Редакторы были в восторге — теперь они могли работать без лишних кликов.
Ключевые характеристики обоих механизмов:
| Характеристика | Cookies | localStorage |
|---|---|---|
| Ёмкость | ~4KB | ~5MB |
| Срок хранения | Настраиваемый | Без срока (до очистки) |
| Доступность | Все окна, отправляется на сервер | Все окна, только клиент |
| API | document.cookie (строка) | localStorage (объект) |
| Безопасность | Флаги HttpOnly, Secure, SameSite | Только Same-Origin Policy |
Для проверки доступности технологий в браузере используйте следующий код:
// Проверка поддержки localStorage
function isLocalStorageAvailable() {
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
return true;
} catch(e) {
return false;
}
}
// Проверка поддержки cookies
function areCookiesEnabled() {
document.cookie = "testcookie=1";
return document.cookie.indexOf("testcookie=") !== -1;
}
Эти технологии различаются не только техническими параметрами, но и сценариями применения. Дальше разберем, как с ними работать на практике.

Запись и извлечение данных: синтаксис и ограничения
Работа с клиентским хранилищем требует понимания синтаксиса и осознания ограничений каждого метода. Разберем, как правильно записывать, читать и удалять данные из cookies и localStorage. 📝
Начнем с cookies. Работа с ними может показаться непривычной для новичков, поскольку API не самый интуитивный:
// Создание cookie
document.cookie = "username=John Doe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
// Чтение всех cookies
const allCookies = document.cookie; // Возвращает строку со всеми cookies
// Получение конкретного значения cookie
function getCookie(name) {
const matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
));
return matches ? decodeURIComponent(matches[1]) : undefined;
}
// Удаление cookie
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
localStorage имеет гораздо более простой и интуитивный API:
// Сохранение данных
localStorage.setItem('username', 'John Doe');
// Чтение данных
const username = localStorage.getItem('username');
// Удаление конкретного ключа
localStorage.removeItem('username');
// Очистка всего хранилища
localStorage.clear();
Хотя localStorage предлагает простой API, оба механизма имеют существенные ограничения, о которых следует помнить:
- Тип данных: И cookies, и localStorage хранят только строки. Для хранения объектов или массивов необходимо использовать JSON.stringify() и JSON.parse()
- Размер хранилища: Cookies ограничены примерно 4KB, localStorage обычно предлагает около 5MB
- Кросс-доменный доступ: Оба механизма связаны с конкретным доменом и не позволяют обмен данными между разными доменами
- Количество cookies: Браузеры ограничивают число cookies на домен (обычно до 50-60)
Практический пример работы с объектами:
// Сохранение объекта в localStorage
const user = {
name: 'John',
age: 30,
preferences: {
theme: 'dark',
notifications: true
}
};
localStorage.setItem('user', JSON.stringify(user));
// Извлечение объекта
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.preferences.theme); // "dark"
// То же самое для cookies
document.cookie = `userObj=${JSON.stringify(user)}; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/`;
// Но будьте осторожны с размером объекта для cookies!
При работе с большими объемами данных следует учитывать не только ограничения по размеру, но и влияние на производительность:
| Операция | Cookies | localStorage | Влияние на производительность |
|---|---|---|---|
| Запись 1KB данных | ~0.5мс | ~0.1мс | Низкое |
| Чтение 1KB данных | ~0.7мс | ~0.1мс | Низкое |
| Запись 100+ значений | ~50мс | ~10мс | Среднее |
| Хранение 5MB данных | Невозможно | Возможно, но замедляет загрузку | Высокое |
Если вы столкнулись с ограничениями localStorage по размеру, но не хотите использовать более сложные решения вроде IndexedDB, можно реализовать простую стратегию "разделения" данных:
// Разделение больших данных на части
function storeChunks(key, data, chunkSize = 1024 * 1024) {
const stringData = JSON.stringify(data);
const chunks = [];
for (let i = 0; i < stringData.length; i += chunkSize) {
chunks.push(stringData.slice(i, i + chunkSize));
}
localStorage.setItem(`${key}_chunks`, chunks.length.toString());
for (let i = 0; i < chunks.length; i++) {
localStorage.setItem(`${key}_chunk_${i}`, chunks[i]);
}
}
// Восстановление данных из частей
function getChunks(key) {
const chunksCount = parseInt(localStorage.getItem(`${key}_chunks`), 10);
if (!chunksCount) return null;
let fullData = '';
for (let i = 0; i < chunksCount; i++) {
fullData += localStorage.getItem(`${key}_chunk_${i}`);
}
return JSON.parse(fullData);
}
Жизненный цикл данных: управление сроком действия
Управление жизненным циклом данных — ключевой аспект работы с клиентским хранилищем. От правильно настроенных сроков действия зависит не только пользовательский опыт, но и безопасность приложения. ⏱️
Cookies предлагают гибкие механизмы управления сроком действия:
// Cookie с явным сроком действия (expires)
document.cookie = "username=John; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
// Cookie с относительным сроком (max-age в секундах)
document.cookie = "session=abc123; max-age=3600; path=/"; // Живет 1 час
// Session cookie (удаляется при закрытии браузера)
document.cookie = "tempData=value; path=/"; // Без expires или max-age
Для localStorage ситуация иная — данные сохраняются бессрочно, пока не будут явно удалены пользователем или программным кодом:
// Данные сохранятся "навечно"
localStorage.setItem('userPreferences', JSON.stringify({theme: 'dark'}));
// Единственный способ задать срок действия — реализовать собственный механизм
function setItemWithExpiry(key, value, ttl) {
const item = {
value: value,
expiry: new Date().getTime() + ttl,
};
localStorage.setItem(key, JSON.stringify(item));
}
function getItemWithExpiry(key) {
const itemStr = localStorage.getItem(key);
if (!itemStr) return null;
const item = JSON.parse(itemStr);
const now = new Date().getTime();
if (now > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value;
}
// Пример: сохраняем данные на 24 часа
setItemWithExpiry('authToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC', 24 * 60 * 60 * 1000);
Понимание жизненного цикла важно для разных типов данных. Вот рекомендации по срокам хранения:
- Авторизационные токены: От нескольких часов до дней (зависит от чувствительности)
- Настройки интерфейса: Длительное хранение (месяцы или годы)
- Корзины покупок: 1-2 недели (или до завершения заказа)
- Данные для автозаполнения форм: 1-3 месяца
- Кэшированные API-ответы: Минуты или часы (зависит от частоты обновления данных)
Мария Соколова, Senior Frontend Engineer
Работая над платформой для онлайн-образования, мы столкнулись с неожиданной проблемой. Студенты жаловались, что их прогресс в курсах постоянно сбрасывается, хотя в базе данных всё сохранялось корректно.
Расследование показало, что для быстрого отображения прогресса мы использовали localStorage без механизма синхронизации с сервером. Студенты часто работали с платформой на разных устройствах, и каждое устройство имело свою локальную копию прогресса.
Мы разработали систему с "умным" управлением жизненным циклом данных. Теперь при входе пользователя мы сравниваем timestamp локальных данных с серверными и применяем стратегию "новейшие данные побеждают". Кроме того, добавили механизм периодической очистки устаревших данных — если студент не открывал курс более 30 дней, локальный кэш сбрасывается и загружается актуальная информация с сервера. Количество жалоб сократилось на 94%, а показатель успешного завершения курсов вырос на 17%.
Управление устаревшими данными — важный аспект поддержки производительности. Реализуйте регулярную проверку и очистку:
// Функция для очистки устаревших данных в localStorage
function cleanupExpiredData() {
// Получаем все ключи
const keys = Object.keys(localStorage);
const now = new Date().getTime();
keys.forEach(key => {
// Проверяем только те ключи, которые соответствуют нашему формату
if (key.startsWith('temp_') || key.endsWith('_expiry')) {
try {
const itemStr = localStorage.getItem(key);
const item = JSON.parse(itemStr);
// Если у элемента есть поле expiry и оно истекло
if (item && item.expiry && now > item.expiry) {
localStorage.removeItem(key);
console.log(`Очищены устаревшие данные: ${key}`);
}
} catch (e) {
// Игнорируем ошибки парсинга
}
}
});
}
// Вызываем функцию очистки при загрузке и периодически
window.addEventListener('load', cleanupExpiredData);
// Запускаем очистку раз в день
setInterval(cleanupExpiredData, 24 * 60 * 60 * 1000);
Безопасность при хранении данных в браузере
Хранение данных на стороне клиента сопряжено с рисками безопасности, о которых многие разработчики узнают лишь после инцидента. Рассмотрим основные уязвимости и методы защиты. 🔐
Первое правило безопасности клиентского хранилища: никогда не храните чувствительные данные в открытом виде. Злоумышленники могут получить доступ к данным через:
- XSS (Cross-Site Scripting) атаки
- Физический доступ к устройству
- Вредоносные расширения браузера
- Man-in-the-middle атаки (для незащищенных cookie)
Для cookies существует несколько важных флагов безопасности:
// HttpOnly – запрещает доступ к cookie через JavaScript
// Secure – отправляет cookie только по HTTPS
// SameSite – защита от CSRF атак
document.cookie = "authToken=abc123; HttpOnly; Secure; SameSite=Strict; path=/";
// SameSite имеет три значения:
// Strict – только при переходе в пределах одного сайта
// Lax – при переходе по ссылке с другого сайта (значение по умолчанию)
// None – разрешено при любых запросах (требует Secure флага)
localStorage не имеет встроенных механизмов защиты, кроме Same-Origin Policy. Если требуется хранить чувствительные данные, используйте шифрование:
// Простая функция шифрования (для демонстрации)
// В реальном проекте используйте WebCrypto API
function encrypt(text, secretKey) {
// Это упрощенная демонстрация, не для реального применения!
return btoa(text.split('').map((char, index) => {
return String.fromCharCode(char.charCodeAt(0) ^ secretKey.charCodeAt(index % secretKey.length));
}).join(''));
}
function decrypt(encryptedText, secretKey) {
return atob(encryptedText).split('').map((char, index) => {
return String.fromCharCode(char.charCodeAt(0) ^ secretKey.charCodeAt(index % secretKey.length));
}).join('');
}
// Применение
const sensitiveData = "123-45-6789"; // Например, номер соц. страхования
const userKey = "user_specific_key"; // Ключ, уникальный для пользователя
localStorage.setItem('encrypted_data', encrypt(sensitiveData, userKey));
// Для извлечения
const retrieved = decrypt(localStorage.getItem('encrypted_data'), userKey);
Для серьезных приложений используйте современные криптографические алгоритмы через WebCrypto API:
async function encryptData(data, password) {
const enc = new TextEncoder();
const passwordKey = await crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
const salt = crypto.getRandomValues(new Uint8Array(16));
const key = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256"
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv
},
key,
enc.encode(data)
);
return {
salt: Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join(''),
iv: Array.from(iv).map(b => b.toString(16).padStart(2, '0')).join(''),
data: Array.from(new Uint8Array(encrypted))
.map(b => b.toString(16).padStart(2, '0')).join('')
};
}
Контрольный список безопасности для работы с клиентским хранилищем:
| Уязвимость | Метод защиты | Применимость |
|---|---|---|
| XSS-атаки | Валидация ввода, Content-Security-Policy, HttpOnly для cookies | Высокий приоритет |
| CSRF-атаки | SameSite cookies, CSRF-токены | Для форм и важных операций |
| Перехват данных | Secure cookies, HTTPS, шифрование чувствительных данных | Для всех данных аутентификации |
| Хранение секретов | Никогда не хранить секреты на клиенте, использовать кратковременные токены | Критический приоритет |
| Кража cookies | HttpOnly, Secure, короткий срок действия для важных cookies | Для аутентификационных cookies |
Дополнительно рекомендуется:
- Регулярно проверять хранимые данные на актуальность и удалять ненужное
- Минимизировать объем хранимой на клиенте информации
- Для критичных операций всегда перепроверять данные на сервере
- Использовать короткие сроки жизни для сессионных токенов
- Внедрить механизм принудительного выхода для всех устройств пользователя
Сравнение cookies и localStorage: когда что применять
Выбор правильного механизма хранения данных критически важен для эффективной работы вашего приложения. Cookies и localStorage имеют различные характеристики, делающие их подходящими для разных сценариев. 🔄
Ключевые отличия, определяющие выбор технологии:
| Критерий | Cookies | localStorage |
|---|---|---|
| Автоматическая отправка на сервер | Да (с каждым запросом) | Нет |
| Объем хранимых данных | ~4KB | ~5MB |
| Срок хранения | Настраиваемый (включая сессионные) | Постоянный (требует ручной очистки) |
| Доступность между вкладками | Да | Да |
| Влияние на производительность | Отправка с каждым запросом (overhead) | Только локальные операции |
| Безопасность | Настраиваемые флаги (HttpOnly, Secure, SameSite) | Только Same-Origin Policy |
| API | Сложный (строковый) | Простой (объектный) |
Cookies лучше использовать для:
- Аутентификации и авторизации — токены сессий отправляются на сервер автоматически
- Отслеживания состояния сервера — когда информация нужна для обработки запросов
- Персонализации на стороне сервера — например, локализация контента
- Трекинга пользователей — аналитика и отслеживание поведения
- Данных с коротким сроком жизни — благодаря управлению временем хранения
localStorage подходит для:
- Пользовательских настроек интерфейса — тема, размер шрифта, расположение элементов
- Кэширования данных приложения — часто используемые данные, не требующие серверной валидации
- Состояния форм — сохранение черновиков и недозаполненных форм
- Offline-функциональности — хранение данных для работы без интернета
- Больших объемов данных — когда 4KB cookies недостаточно
Оптимальные комбинированные подходы:
// Пример 1: Аутентификация и пользовательские настройки
// Токен в cookie для авторизации
document.cookie = "authToken=eyJhbGciOiJIUzI1NiI...; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; path=/";
// Настройки интерфейса в localStorage
localStorage.setItem('userPreferences', JSON.stringify({
theme: 'dark',
fontSize: 'large',
sidebar: 'collapsed'
}));
// Пример 2: Корзина покупок с синхронизацией
// ID корзины в cookie для идентификации на сервере
document.cookie = "cartId=cart_12345; Secure; SameSite=Lax; Max-Age=1209600; path=/";
// Содержимое корзины в localStorage для быстрого доступа
// и работы offline
localStorage.setItem('cartItems', JSON.stringify([
{id: "prod1", quantity: 2, price: 19.99},
{id: "prod2", quantity: 1, price: 29.99}
]));
// При изменении корзины синхронизируем с сервером
function updateCart(items) {
localStorage.setItem('cartItems', JSON.stringify(items));
// Отправляем на сервер (cartId берется из cookie автоматически)
fetch('/api/cart/update', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({items})
});
}
При выборе технологии также учитывайте:
- Требования к безопасности — для защиты чувствительных данных cookies с HttpOnly более безопасны
- Объем трафика — cookies отправляются с каждым запросом, что может влиять на производительность
- Время жизни данных — localStorage требует ручного управления устареванием
- Необходимость доступа с сервера — только cookies доступны серверу автоматически
- Совместимость с браузерами — localStorage не работает в режиме приватного просмотра в некоторых браузерах
В современной разработке часто используется гибридный подход: cookies для аутентификации и критичных данных, localStorage для пользовательского интерфейса и кэширования. Такое разделение обеспечивает оптимальный баланс между безопасностью и производительностью.
Освоив технологии клиентского хранилища данных, вы значительно расширяете арсенал своих возможностей как веб-разработчик. Cookies и localStorage — это не просто способы сохранить пару значений, а мощные инструменты для создания персонализированного, быстрого и отзывчивого пользовательского опыта. Грамотно комбинируя эти технологии с учетом их сильных сторон и ограничений, вы сможете реализовать элегантные решения для самых сложных задач веб-разработки. И помните — лучший подход всегда тот, который учитывает специфику именно вашего проекта.