Вступление: «Почему моя сессия не умирает?»
Представьте: вы пишете интернет-магазин, админку или личный кабинет. Пользователь вошёл, поработал, ушёл пить кофе — а через час его сессия всё ещё жива. Данные в корзине висят, доступ открыт. Это не только неудобно, но и опасно. Особенно если сайт работает с платёжными данными.
Вопрос «How do I expire a PHP session after 30 minutes?» — один из самых популярных на Stack Overflow (десятки тысяч просмотров). И неудивительно: начинающие разработчики часто думают, что достаточно выставить session.gc_maxlifetime — и всё заработает. А потом удивляются, почему сессия живёт часами.
В этой статье мы разберём 4 реальных подхода, как заставить PHP сессию истекать ровно через 30 минут. Вы узнаете, почему стандартные настройки могут подвести, и какой метод выбрать под свою задачу. Поехали!
Немного теории: как PHP управляет временем сессии
Прежде чем писать код, важно понять два механизма, которые влияют на «смерть» сессии:
- Время жизни cookie сессии — сколько браузер хранит идентификатор сессии (PHPSESSID). Управляется параметром
session.cookie_lifetime. - Время жизни данных на сервере — как долго файл сессии лежит в папке /tmp. Управляется
session.gc_maxlifetime.
Проблема в том, что сборщик мусора (GC — garbage collector) запускается вероятностно. По умолчанию он срабатывает в 1% запросов. Это значит, что даже если gc_maxlifetime = 1800 (30 минут), сессия может висеть ещё час, пока GC до неё доберётся.
Поэтому полагаться только на серверную настройку — плохая идея. Нужно проверять время «вручную».
Способ №1. Классический: проверка времени в сессии (рекомендуемый)
Это самый надёжный и прозрачный способ. Вы сами храните в сессии метку времени последней активности, и при каждом запросе проверяете, не прошло ли 30 минут.
<?php
session_start();
// Устанавливаем время жизни сессии — 30 минут (1800 секунд)
$timeout_duration = 1800;
// Если в сессии есть метка времени и она просрочена
if (isset($_SESSION['LAST_ACTIVITY']) &&
(time() - $_SESSION['LAST_ACTIVITY']) > $timeout_duration) {
// Уничтожаем сессию
session_unset();
session_destroy();
// Перенаправляем на страницу логина
header("Location: login.php?timeout=1");
exit;
}
// Обновляем метку времени (если пользователь активен)
$_SESSION['LAST_ACTIVITY'] = time();
?>
Плюсы:
- Абсолютный контроль: сессия умрёт ровно через 30 минут бездействия.
- Не зависит от настроек PHP и сборщика мусора.
- Легко менять время под разные разделы сайта (например, админка — 15 минут, пользователь — 60).
Минусы:
- Нужно добавлять проверку на каждую страницу (или вынести в общий файл инициализации).
- Если пользователь держит страницу открытой, но не кликает — сессия умрёт. Для некоторых сценариев (долгая форма) это минус.
Способ №2. Комбинированный: gc_maxlifetime + cookie_lifetime
Если вы не хотите писать ручную проверку, можно попробовать настроить PHP «из коробки». Но помните о нюансах.
// В начале скрипта, до session_start()
$lifetime = 1800; // 30 минут
// Устанавливаем время жизни cookie сессии
session_set_cookie_params($lifetime);
// Устанавливаем максимальное время жизни данных на сервере
ini_set('session.gc_maxlifetime', $lifetime);
// Теперь стартуем сессию
session_start();
Дополнительно — чтобы GC работал чаще, можно уменьшить вероятность запуска:
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 1); // GC будет запускаться при каждом запросе
Важно! Установка gc_divisor = 1 может слегка нагрузить сервер на высоконагруженных проектах. Для маленьких сайтов — OK.
Плюсы:
- Минимум кода.
- Работает «из коробки» на стандартных хостингах.
Минусы:
- Cookie живёт 30 минут с момента создания, а не с момента последнего действия. Если пользователь активен, cookie всё равно умрёт через 30 минут.
- GC может не успеть подчистить старые сессии (если не менять gc_probability).
- Не подходит, если нужно продлевать сессию при каждом действии.
Способ №3. Продвинутый: регенерация ID сессии + скользящее окно
Этот подход часто используют банковские и платёжные системы. Суть: при каждом действии пользователя мы не только проверяем время, но и регенерируем ID сессии, чтобы предотвратить фиксацию сессии (session fixation).
<?php
session_start();
$timeout = 1800; // 30 минут
$regenerate_time = 300; // регенерировать ID каждые 5 минут
// Проверка на таймаут
if (isset($_SESSION['LAST_ACTIVITY']) &&
(time() - $_SESSION['LAST_ACTIVITY'] > $timeout)) {
session_unset();
session_destroy();
header("Location: login.php?timeout=1");
exit;
}
// Регенерация ID сессии для безопасности
if (!isset($_SESSION['CREATED'])) {
$_SESSION['CREATED'] = time();
} else if (time() - $_SESSION['CREATED'] > $regenerate_time) {
session_regenerate_id(true);
$_SESSION['CREATED'] = time();
}
// Обновляем метку последней активности
$_SESSION['LAST_ACTIVITY'] = time();
?>
Плюсы:
- Максимальная безопасность: ID сессии меняется каждые 5 минут.
- Скользящее окно: сессия продлевается при каждом действии.
- Идеально для интернет-банков и кабинетов с деньгами.
Минусы:
- Больше кода.
- Требует понимания безопасности сессий (не для новичков).
Способ №4. Хардкорный: ручное управление файлами сессий
Этот метод используется, когда стандартные механизмы PHP не подходят (например, вы храните сессии в базе