Why Store PHP Sessions in Redis Across Multiple Servers
The default PHP session handler writes files to the /var/lib/php/sessions directory on the local disk of the server. As long as the application runs on a single machine, this is not a problem. As soon as a load balancer puts two or more web servers behind it, a user's next request can land on a different server and lose the session: the file with its data stayed on the first host.
The solution is to move session storage outside the local disk into a shared store available to all servers at once. Redis suits this better than the NFS file system: it keeps data in memory, responds in fractions of a millisecond, and supports TTL out of the box, so expired sessions get removed automatically. Setting up web servers with Nginx and PHP-FPM, for example following the scheme from the article Laravel on a server with Nginx, usually leads to exactly this need for shared session storage.
How to Enable Redis as the Session Handler
PHP can work with Redis as session.save_handler without any extra code in the application — two directives in php.ini or in the PHP-FPM pool are enough. The first sets the handler itself, the second the connection address.
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?timeout=2.5&persistent=1"
The persistent=1 parameter keeps the connection to Redis open between PHP-FPM requests and saves time on establishing a TCP connection. If Redis runs on a separate server instead of locally, replace 127.0.0.1 with its internal IP address and be sure to close port 6379 from the outside world with firewall rules.
Installing the php-redis Module and Checking It
The session.save_handler = redis directive will not work on its own — you need the php-redis extension built for the installed PHP version.
apt install php-redis
systemctl restart php8.3-fpm
php -m | grep redis
If the server runs several PHP versions at once, install the extension for each version separately — the details of such a setup are covered in the article on running multiple PHP versions on one server. After installation, restart the PHP-FPM pool, otherwise the workers will keep using the old module list for hours.
session.save_path Parameters for Redis
The connection string supports several parameters joined with an ampersand. Below are the ones that actually matter in production.
| Parameter | Example | Purpose |
|---|---|---|
| timeout | 2.5 | Connection timeout to Redis, in seconds |
| persistent | 1 | Keep the connection open between requests |
| weight | 1 | Server weight when using several Redis nodes |
| prefix | sess_ | Prefix for session keys in a shared Redis database |
Always set a prefix if the same Redis database also holds application cache data: without it, session keys and cache keys are easy to mix up when clearing with the FLUSHDB command.
Common Mistakes When Moving Sessions to Redis
- Forgetting to restart PHP-FPM after editing php.ini — old workers keep writing sessions to files.
- Redis is configured without a password and listens on 0.0.0.0 — every user's session is reachable from the internet.
- No maxmemory-policy is set, so Redis starts evicting session keys like ordinary cache when memory runs low.
- The session ID is passed in the URL instead of a cookie — someone else's session ends up in logs and browser history.
- Servers have different clocks (no NTP sync), which makes the session TTL count incorrectly.
Summary: Checklist for Moving Sessions to Redis
- session.save_handler = redis and session.save_path are identical on every web server.
- The php-redis extension is installed and enabled for every PHP version in use.
- Redis is protected with a requirepass password and closed off by the firewall from outside connections.
- A maxmemory-policy of noeviction or volatile-ttl is set for the database that holds sessions.
- After the changes, PHP-FPM was restarted and phpinfo confirms sessions are indeed written to Redis.