Setting a dedicated PHP session directory keeps file-backed session data out of shared temporary locations and makes permissions, cleanup, and per-application isolation easier to control. That matters on shared hosts, multi-pool PHP-FPM deployments, and any stack where session files should stay with the application instead of blending into general-purpose temporary storage.
When session.save_handler is files, PHP writes one sess_* file per active session under the directory resolved by session.save_path. The effective value can come from the main php.ini file, scanned .ini fragments, PHP-FPM pool directives, web-server FastCGI overrides, per-directory .user.ini files, or application code, so the final check has to come from the same web-facing runtime that serves the site.
The new path also needs ownership and permissions that match the runtime user, and session expiry needs a cleanup path that still reaches the new directory. Some hosts run garbage collection during requests while others rely on an OS-level timer or cron job, so a pool-specific override may need matching cleanup logic. Separate directories are also safer when different pools or applications use different owners or session.gc_maxlifetime values.
Related: How to set PHP open_basedir for a PHP-FPM pool
Related: How to create a PHP-FPM pool
$ php-fpm8.3 -i | grep -E 'Loaded Configuration File|^(session.gc_probability|session.save_handler|session.save_path) =>' Loaded Configuration File => /etc/php/8.3/fpm/php.ini session.gc_probability => 0 => 0 session.save_handler => files => files session.save_path => /var/lib/php/sessions => /var/lib/php/sessions
Use php-fpm instead of php-fpm8.3 when the local binary is not versioned. The php --ri session and php --ini commands only report the CLI SAPI, which can differ from the runtime that serves web requests.
If session.gc_probability is 0, old sessions may be cleaned by a distro timer or cron job instead of request-time garbage collection.
If the handler is already redis, memcached, or another non-files backend, changing session.save_path will not move the active session store.
Related: How to find PHP configuration files
$ sudo grep -REn 'php_(admin_)?value\[(session.save_path|session.save_handler|session.gc_maxlifetime)\]' /etc/php/8.3/fpm/pool.d /etc/php/8.3/fpm/pool.d/www.conf:492:php_admin_value[session.save_path] = /var/lib/php/www-session
No output means the pool files are not overriding these directives. If a pool file already sets session.save_path, update that pool instead of assuming the global php.ini controls the site.
When the host relies on a timer or cron job for old-session cleanup, confirm that job follows the new directory if the path is changed only in a pool override.
$ sudo cp /etc/php/8.3/fpm/php.ini /etc/php/8.3/fpm/php.ini.bak-$(date +%Y%m%d%H%M%S)
$ sudo install -d -m 700 -o www-data -g www-data /var/lib/php/app-session $ ls -ld /var/lib/php/app-session drwx------ 2 www-data www-data 4096 Mar 25 23:03 /var/lib/php/app-session
Avoid broad locations such as /tmp on multi-user hosts because another local account could read or replace session files there.
Use a separate directory per pool or application when owners, group permissions, or session.gc_maxlifetime differ.
$ sudoedit /etc/php/8.3/fpm/php.ini
session.save_path = "/var/lib/php/app-session"
Keep the setting in the FPM php.ini or a scanned conf.d file when the host cleanup routine needs to discover the custom path automatically. Use a pool override only when the change must stay pool-specific.
The optional N;MODE;/path format changes directory depth and file mode. Keep the value quoted when semicolons are present, and remember that automatic garbage collection does not run when N is greater than 0.
$ sudo php-fpm8.3 -t [25-Mar-2026 23:03:10] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
Do not reload the service until the configuration test succeeds.
$ sudo systemctl reload php8.3-fpm
Use the matching unit name for the installed package, such as php-fpm on many Fedora, Red Hat, or CentOS Stream hosts. Reload Apache instead when PHP runs as an Apache module, and skip the service reload entirely for a CLI-only change.
$ sudo tee /var/www/example.com/public/session-path-check.php >/dev/null <<'PHP'
<?php
session_start();
header('Content-Type: text/plain');
echo session_save_path(), PHP_EOL;
PHP
Replace /var/www/example.com/public with the document root for the site that uses this pool. Using a web request for the final check confirms the same SAPI, pool, and FastCGI overrides that serve live traffic.
Remove the probe file after verification so session configuration details are not left exposed over HTTP.
$ curl -s http://example.com/session-path-check.php /var/lib/php/app-session $ find /var/lib/php/app-session -maxdepth 1 -type f -name 'sess_*' /var/lib/php/app-session/sess_d83e17r0qssa74fsi7vc2chtei
If the returned path is still the old value, check for a pool override, a web-server PHP_VALUE or PHP_ADMIN_VALUE directive, a .user.ini entry, or application code that calls session_save_path() or ini_set() before session_start().
$ sudo rm /var/www/example.com/public/session-path-check.php