Сессии и куки в PHP: что нужно знать до начала
Разбираемся, чем куки отличаются от сессий в PHP 8.x, как запустить сессию без ошибки headers already sent и какие флаги безопасности выставить с первого дня.

За 20 минут вы поднимете рабочую сессию на PHP 8.x, научитесь читать и писать куки и разберётесь, почему появляется классическая ошибка headers already sent. Всё, что нужно — установленный PHP 8.1 или новее и любой веб-сервер (встроенный php -S подойдёт для проверки). Прав администратора не требуется, но доступ к php.ini или возможность менять его через ini_set() пригодится.
Что нужно иметь до начала
- PHP версии 8.1 или новее — проверьте командой
php -v. Многое ниже работает и на 7.x, но флагSameSiteв удобном виде появился только в 7.3. - Расширение сессий включено по умолчанию, отдельно ставить ничего не нужно.
- Возможность отправлять заголовки до вывода HTML — то есть в файле не должно быть ни одного символа (даже пробела или BOM) перед первым обращением к сессии или куке.
Куки и сессия — это не одно и то же
Кука — это небольшая строка, которую сервер просит браузер сохранить и присылать обратно с каждым запросом. Живёт она на стороне клиента, и пользователь может её увидеть и подделать. Сессия хранится на сервере (по умолчанию — в файле во временной папке), а браузеру отдаётся только идентификатор — как раз в куке с именем PHPSESSID. Поэтому логин и права доступа держат в сессии, а не в куке: подделать серверный файл сложнее, чем строку в браузере.
Правило, которое экономит часы отладки: всё, что связано с сессиями и куками, работает через HTTP-заголовки. Заголовки уходят до тела ответа. Один пробел перед <?php — и заголовки уже отправить нельзя.
Шаг 1. Запускаем сессию
Сессия не стартует сама. Вызовите session_start() в самом начале скрипта, до любого вывода:
<?php
// index.php — PHP 8.x
// Стартуем сессию до вывода чего-либо в браузер
session_start();
// Записываем данные
$_SESSION['user_id'] = 42;
$_SESSION['role'] = 'editor';
echo 'Сессия запущена, ID: ' . session_id();
Как понять, что получилось: в браузере появится строка вида «Сессия запущена, ID: a1b2c3...», а в инструментах разработчика (вкладка Application → Cookies) увидите куку PHPSESSID.
Шаг 2. Читаем данные на другой странице
На любой другой странице снова вызываете session_start() — он не создаёт новую сессию, а подхватывает существующую по PHPSESSID:
<?php
// profile.php
session_start();
if (isset($_SESSION['user_id'])) {
echo 'Вы вошли как пользователь #' . $_SESSION['user_id'];
} else {
echo 'Вы не авторизованы';
}
Как понять, что получилось: открыв profile.php после index.php в том же браузере, увидите номер пользователя. В режиме инкогнито (новая сессия) — «Вы не авторизованы».
Шаг 3. Ставим свою куку
Функция setcookie() в PHP 7.3+ принимает массив опций — так удобнее выставлять флаги безопасности:
<?php
// Устанавливаем куку на 7 дней с флагами безопасности
setcookie('theme', 'dark', [
'expires' => time() + 7 * 24 * 60 * 60, // срок жизни
'path' => '/',
'httponly' => true, // недоступна из JavaScript
'secure' => true, // только по HTTPS
'samesite' => 'Lax', // защита от части CSRF
]);
// Прочитать куку можно только на СЛЕДУЮЩЕМ запросе
if (isset($_COOKIE['theme'])) {
echo 'Тема: ' . $_COOKIE['theme'];
}
Важная ловушка новичка: только что установленная кука не появится в $_COOKIE в том же запросе. Браузер пришлёт её лишь со следующим обращением. Так что echo выше сработает начиная со второй загрузки страницы.
Шаг 4. Настраиваем безопасность сессионной куки
Кука PHPSESSID тоже должна быть защищена. Задайте параметры до session_start():
<?php
// Настройки должны идти ДО session_start()
session_set_cookie_params([
'lifetime' => 0, // до закрытия браузера
'path' => '/',
'httponly' => true,
'secure' => true, // включайте на HTTPS-сайте
'samesite' => 'Lax',
]);
session_start();
Флаг httponly закрывает доступ к куке из JavaScript, что снижает ущерб от XSS. Флаг secure запрещает отправлять куку по обычному HTTP — включайте его только на сайтах с HTTPS, иначе на локальном http://localhost сессия перестанет работать.
Куда PHP кладёт сессии
По умолчанию сессии хранятся файлами во временной директории (путь задаёт session.save_path в php.ini). Это работает на одном сервере. Как только у вас несколько серверов за балансировщиком, файловое хранилище ломается: запрос попадает на другую машину, где файла сессии нет. Тогда переходят на общее хранилище — Redis или базу данных через session.save_handler. Настройка внешнего хранилища — отдельная тема, здесь остаёмся на файлах по умолчанию.
Когда что использовать
| Задача | Куки | Сессия |
|---|---|---|
| Хранение логина и прав | Нет — легко подделать | Да |
| Выбор темы оформления | Да — не критично | Избыточно |
| Корзина до авторизации | Можно, с осторожностью | Да, надёжнее |
| Флаг «запомнить меня» | Да, с длинным сроком | Нет — истекает |
Если не получилось
- Warning: session_start(): ... headers already sent — до вызова уже что-то выведено. Ищите пробелы или пустые строки перед
<?php, лишний вывод, а также BOM в начале файла (сохраните файл в UTF-8 без BOM). - Кука не сохраняется — при
'secure' => trueнаhttp://браузер её игнорирует. Проверьте, что сайт на HTTPS, или временно уберите флаг для локальной разработки. - $_SESSION пустой на другой странице — не вызвали
session_start()на этой странице, либо разныеpath/domainу куки, либо браузер режет сторонние куки из-заSameSite. - Notice: Undefined array key при чтении
$_SESSION['...']— обращаетесь к ключу, которого нет. Проверяйте черезisset()перед чтением. - Данные теряются случайно — часто дело в
session.gc_maxlifetime: сборщик мусора удаляет старые файлы сессий. Значение задаётся в секундах вphp.ini.
Что делать дальше
Когда базовый вход работает, сделайте три вещи. Первое — при логине вызывайте session_regenerate_id(true), чтобы сменить идентификатор и закрыть угрозу фиксации сессии. Второе — при выходе очищайте $_SESSION и удаляйте куку PHPSESSID, а не просто обнуляйте массив. Третье — если планируете больше одного сервера, заранее посмотрите документацию по session.save_handler и хранению сессий в Redis. Точные имена параметров и их поведение в вашей версии сверяйте в официальной документации PHP по разделам «Session Handling» и функции setcookie.
Инфографика для маркетплейса: Три главных преимущества товара короткими фразами, выноски в безопасной зоне превью, точный размер площадки.
Спросить за 60 ₽Источники: PHP Manual: Session Handling, PHP Manual: setcookie
Частые вопросы
У меня PHP 7.2, массив опций в setcookie не работает — что делать?
Почему кука не появляется в $_COOKIE сразу после setcookie?
Я включил secure и сессия перестала работать локально — это баг?
Как правильно разлогинить пользователя?
Безопасно ли хранить сессии в файлах на проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.