Зачем хранить сессии PHP в Redis на нескольких серверах
Стандартный обработчик сессий PHP пишет файлы в каталог /var/lib/php/sessions на локальном диске сервера. Пока приложение работает на одной машине, это не проблема. Как только за балансировщиком нагрузки появляются два и больше веб-сервера, пользователь при следующем запросе может попасть на другой сервер и потерять сессию: файл с её данными остался на первом хосте.
Решение — вынести хранилище сессий за пределы локального диска в общее хранилище, доступное всем серверам сразу. Redis подходит для этого лучше файловой системы NFS: он держит данные в памяти, отвечает за доли миллисекунды и поддерживает TTL из коробки, поэтому просроченные сессии удаляются сами. Настройка веб-серверов под Nginx и PHP-FPM, например по схеме из статьи Laravel на сервере с Nginx, обычно и приводит к необходимости общего хранилища сессий.
Как включить Redis как обработчик сессий
PHP умеет работать с Redis как с session.save_handler без дополнительного кода в приложении — достаточно двух директив в php.ini или в пуле PHP-FPM. Первая задаёт сам обработчик, вторая — адрес подключения.
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?timeout=2.5&persistent=1"
Параметр persistent=1 держит соединение с Redis открытым между запросами PHP-FPM и экономит время на установке TCP-соединения. Если Redis стоит на отдельном сервере, а не локально, замените 127.0.0.1 на его внутренний IP и обязательно закройте порт 6379 от внешнего мира правилами firewall.
Установка модуля php-redis и проверка
Директива session.save_handler = redis не заработает сама по себе — нужно расширение php-redis, собранное для установленной версии PHP.
apt install php-redis
systemctl restart php8.3-fpm
php -m | grep redis
Если сервер обслуживает несколько версий PHP одновременно, ставьте расширение под каждую версию отдельно — детали такой конфигурации разобраны в статье о нескольких версиях PHP на одном сервере. После установки перезапустите пул PHP-FPM, иначе воркеры будут работать со старым списком модулей ещё несколько часов.
Параметры session.save_path для Redis
Строка подключения поддерживает несколько параметров через амперсанд. Ниже — те, что реально пригождаются на проде.
| Параметр | Пример | Назначение |
|---|---|---|
| timeout | 2.5 | Таймаут подключения к Redis в секундах |
| persistent | 1 | Держать соединение между запросами |
| weight | 1 | Вес сервера при нескольких узлах Redis |
| prefix | sess_ | Префикс ключей сессий в общей базе Redis |
Префикс prefix стоит задавать всегда, если в той же базе Redis лежат ещё и данные кэша приложения: без него ключи сессий и кэша легко перепутать при чистке командой FLUSHDB.
Типичные ошибки при переносе сессий в Redis
- Забыли перезапустить PHP-FPM после правки php.ini — старые воркеры продолжают писать сессии в файлы.
- Redis настроен без пароля и слушает 0.0.0.0 — сессии всех пользователей доступны из интернета.
- Не задан maxmemory-policy, из-за чего Redis при нехватке памяти начинает выселять ключи сессий как обычный кэш.
- Session ID передаётся в URL вместо cookie — в логах и истории браузера остаётся чужая сессия.
- Между серверами разное время (нет синхронизации NTP), из-за чего TTL сессии считается некорректно.
Итог: чек-лист переноса сессий в Redis
- session.save_handler = redis и session.save_path указаны одинаково на всех веб-серверах.
- Расширение php-redis установлено и подключено для каждой используемой версии PHP.
- Redis защищён паролем requirepass и закрыт firewall от внешних подключений.
- Задан maxmemory-policy noeviction или volatile-ttl для базы с сессиями.
- После изменений выполнен перезапуск PHP-FPM и проверка через phpinfo, что сессии действительно пишутся в Redis.