Навіщо зберігати сесії 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.