PHP куки: установка, чтение и удаление (HttpOnly, Secure, SameSite)
Эта статья — практическое руководство по теме «PHP куки: установка, чтение и удаление». Разберём реальный синтаксис функции setcookie, научимся безопасно работать с атрибутами HttpOnly, Secure и SameSite, а также избежим типичных ошибок вроде отправки cookie после вывода HTML. В конце вы получите набор готовых функций для повседневной разработки.
Что такое куки и когда их использовать
Куки (cookies) — это небольшие фрагменты данных (до ~4 KB каждый), которые браузер хранит по домену и отправляет на сервер с каждым запросом. В PHP их используют для:
- запоминания настроек пользователя (тема, язык);
- простой персонализации интерфейса;
- корзины гостя без авторизации;
- механик «запомнить меня» (с осторожностью и подписью).
Важно: не храните в cookies чувствительные данные (пароли, токены без подписи).
Быстрый старт: установка cookie через setcookie
Современный и рекомендуемый способ (PHP 7.3+): передавать массив опций.
<?php
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
setcookie('theme', 'dark', [
'expires' => time() + 60 * 60 * 24 * 30, // 30 дней
'path' => '/',
'domain' => '', // по умолчанию текущий хост
'secure' => $isHttps, // true только по HTTPS
'httponly' => true, // cookie недоступны JS (document.cookie)
'samesite' => 'Lax', // Lax | Strict | None (см. ниже)
]);
echo 'Куки установлена!';
Правила:
- setcookie должен вызываться до любого вывода (до HTML, echo, var_dump).
- Если нужен вывод раньше — включайте буферизацию (ob_start) или перестройте логику ответа.
- Для поддоменов используйте 'domain' => '.example.com'.
- Для удаления важно указывать те же path и domain, что и при установке.
Чтение cookie в PHP
Читать куки просто: данные приходят в суперглобальном массиве $_COOKIE. Помните, это строки, и их может не быть.
<?php
$theme = $_COOKIE['theme'] ?? null;
if ($theme === 'dark') {
// подгружаем тёмную тему
}
Обновление и удаление куки
Обновление — это повторная установка с тем же именем и новыми параметрами.
<?php
// обновляем срок жизни темы ещё на 30 дней
setcookie('theme', 'dark', [
'expires' => time() + 60 * 60 * 24 * 30,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
Удаление — это установка прошедшей даты истечения. Обязательно совпадайте по path и domain с оригиналом!
<?php
setcookie('theme', '', [
'expires' => time() - 3600, // в прошлом
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
SameSite, HttpOnly и Secure — как выбрать
- HttpOnly: защитит от кражи cookie через XSS, т.к. JS не сможет прочитать cookie. Включайте для большинства серверных куки.
- Secure: cookie уйдёт только по HTTPS. В продакшене — всегда true.
- SameSite:
- Lax — безопасный дефолт: отправляется при переходах ссылкой, но не при большинстве cross-site запросов.
- Strict — максимально строго, не уходит при переходе с другой вкладки/сайта (может ломать UX).
- None — cookie отправляется во всех кросс-сайтовых запросах, но требуется Secure = true. Используйте только при реальной необходимости (встраивание, SSO).
Готовые утилиты для работы с cookies
Сниппеты, которые можно скопировать в проект. Они учитывают HTTPS и сразу синхронизируют $_COOKIE в текущем запросе.
<?php
function is_https(): bool {
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
}
function cookie_set(string $name, string $value, array $options = []): void {
$defaults = [
'expires' => 0, // 0 = до закрытия браузера
'path' => '/',
'domain' => '',
'secure' => is_https(),
'httponly' => true,
'samesite' => 'Lax',
];
$opts = array_merge($defaults, $options);
setcookie($name, $value, $opts);
// Синхронизируем локально, чтобы сразу видеть новое значение
$_COOKIE[$name] = $value;
}
function cookie_get(string $name, $default = null) {
return $_COOKIE[$name] ?? $default;
}
function cookie_delete(string $name, array $options = []): void {
$defaults = [
'path' => '/',
'domain' => '',
'secure' => is_https(),
'httponly' => true,
'samesite' => 'Lax',
];
$opts = array_merge($defaults, $options);
$opts['expires'] = time() - 3600; // прошлое
setcookie($name, '', $opts);
unset($_COOKIE[$name]);
}
// Пример использования:
cookie_set('lang', 'ru', ['expires' => time() + 86400 * 365]);
$lang = cookie_get('lang', 'en');
// ...
// cookie_delete('lang');
Работа с массивами и сложными значениями
Куки — строки. Для хранения структур используйте JSON + base64, а лучше — подпись (HMAC), чтобы нельзя было подделать значение на клиенте.
<?php
const APP_KEY = 'super-secret-key-change-me'; // храните в .env
function cookie_set_json(string $name, array $data, array $options = []): void {
$payload = base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE));
$sig = hash_hmac('sha256', $payload, APP_KEY);
cookie_set($name, $payload.'.'.$sig, $options);
}
function cookie_get_json(string $name, $default = null) {
$raw = cookie_get($name);
if (!$raw || !str_contains($raw, '.')) return $default;
[$payload, $sig] = explode('.', $raw, 2);
$calc = hash_hmac('sha256', $payload, APP_KEY);
if (!hash_equals($calc, $sig)) return $default; // подпись не совпала
$json = base64_decode($payload, true);
return json_decode($json, true) ?? $default;
}
// Пример: лёгкая персонализация
cookie_set_json('prefs', ['theme' => 'dark', 'font' => '16px'], [
'expires' => time() + 86400 * 30,
]);
$prefs = cookie_get_json('prefs', ['theme' => 'light']);
Если всё же требуется шифрование, используйте проверенные библиотеки (libsodium) и ротируйте ключи. Но чаще на практике достаточно подписи и «не хранить секреты в куки».
Типичные ошибки и как их избежать
- Вывод до setcookie: любая пробельная строка перед <?php или echo ломает заголовки. Решение: подключайте куки в самом начале скрипта, используйте буферизацию или контролируйте порядок вывода.
- Отсутствие Secure в проде: по HTTP куки утекают. Включайте HTTPS и ставьте secure=true.
- SameSite=None без Secure: браузеры блокируют такие куки. Добавьте secure=true.
- Удаление без совпадения path/domain: старая куки не стирается. Укажите такие же атрибуты, как при установке.
- Слишком большие данные: одну куки ограничивайте ~4 KB. Иначе обрезка и ошибки. Храните только то, что нужно.
- Хранение персональных/секретных данных: избегайте, используйте серверное хранилище (сессии, БД), а в куки — только идентификаторы с подписью.
Path, Domain и поддомены
Атрибут path ограничивает видимость куки в рамках URL-пути. Часто ставят '/'. Атрибут domain определяет доменную зону: пустой — текущий хост; '.example.com' — доступно на всех поддоменах. Для удаления используйте те же значения.
<?php
// Доступно на всех поддоменах *.example.com
cookie_set('u', '123', [
'domain' => '.example.com',
'path' => '/',
]);
Куки «remember me»: короткий паттерн
Минимальная безопасная схема — не хранить логин/пароль, а хранить случайный токен + подпись, проверяя его по БД на стороне сервера. При утечке токен можно отозвать.
<?php
// генерация при входе
$token = bin2hex(random_bytes(32)); // сохраняем hash(token) в БД
cookie_set('remember', $token, [
'expires' => time() + 60*60*24*30,
'samesite' => 'Lax',
]);
// при посещении сайта
$token = cookie_get('remember');
if ($token) {
// ищем hash_equals(hashDB, hash(token)) и если ок — авторизуем, обновляем токен
}
Чек-лист перед продакшеном
- HTTPS включён, Secure=true для всех важный куки;
- HttpOnly=true для куки, которые не нужны JS;
- SameSite подобран под сценарий (обычно Lax);
- Не храните в куки секреты и персональные данные;
- Размеры и количество под контролем (браузеры ограничивают ~20–50 куки на домен);
- Удаление совпадает по path/domain;
- Локально валидируете и подписываете сложные значения (HMAC).
Куда двигаться дальше
Если вы хотите системно прокачать навыки PHP и базы данных, рекомендую пройти практический курс с множеством проектов: Прокачать PHP на практике: курс «PHP и MySQL с Нуля до Гуру 3.0». Там вы отработаете работу с куки, сессиями, БД и безопасностью на реальных задачах.
Теперь вы уверенно управляете куки в PHP — от базовой установки до безопасной подписи и удаления. Используйте приведённые шаблоны как основу для своих проектов и не забывайте о безопасности в продакшене.
-
Создано 09.01.2026 17:01:58
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.