Cookies и localStorage: как хранить данные в браузере правильно

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • начинающие веб-разработчики
  • профессиональные разработчики, желающие улучшить свои навыки
  • студенты и обучающиеся, заинтересованные в веб-разработке

    Хранение данных на стороне клиента превращает обычные сайты в динамические, персонализированные платформы. Без 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

Для проверки доступности технологий в браузере используйте следующий код:

JS
Скопировать код
// Проверка поддержки 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 не самый интуитивный:

JS
Скопировать код
// Создание 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:

JS
Скопировать код
// Сохранение данных
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)

Практический пример работы с объектами:

JS
Скопировать код
// Сохранение объекта в 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, можно реализовать простую стратегию "разделения" данных:

JS
Скопировать код
// Разделение больших данных на части
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 предлагают гибкие механизмы управления сроком действия:

JS
Скопировать код
// 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 ситуация иная — данные сохраняются бессрочно, пока не будут явно удалены пользователем или программным кодом:

JS
Скопировать код
// Данные сохранятся "навечно"
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%.

Управление устаревшими данными — важный аспект поддержки производительности. Реализуйте регулярную проверку и очистку:

JS
Скопировать код
// Функция для очистки устаревших данных в 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 существует несколько важных флагов безопасности:

JS
Скопировать код
// 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. Если требуется хранить чувствительные данные, используйте шифрование:

JS
Скопировать код
// Простая функция шифрования (для демонстрации)
// В реальном проекте используйте 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:

JS
Скопировать код
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 недостаточно

Оптимальные комбинированные подходы:

JS
Скопировать код
// Пример 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})
});
}

При выборе технологии также учитывайте:

  1. Требования к безопасности — для защиты чувствительных данных cookies с HttpOnly более безопасны
  2. Объем трафика — cookies отправляются с каждым запросом, что может влиять на производительность
  3. Время жизни данных — localStorage требует ручного управления устареванием
  4. Необходимость доступа с сервера — только cookies доступны серверу автоматически
  5. Совместимость с браузерами — localStorage не работает в режиме приватного просмотра в некоторых браузерах

В современной разработке часто используется гибридный подход: cookies для аутентификации и критичных данных, localStorage для пользовательского интерфейса и кэширования. Такое разделение обеспечивает оптимальный баланс между безопасностью и производительностью.

Освоив технологии клиентского хранилища данных, вы значительно расширяете арсенал своих возможностей как веб-разработчик. Cookies и localStorage — это не просто способы сохранить пару значений, а мощные инструменты для создания персонализированного, быстрого и отзывчивого пользовательского опыта. Грамотно комбинируя эти технологии с учетом их сильных сторон и ограничений, вы сможете реализовать элегантные решения для самых сложных задач веб-разработки. И помните — лучший подход всегда тот, который учитывает специфику именно вашего проекта.

Загрузка...