PHP sessions keep users logged in and carts filled between page loads. When they break you see warnings like session_start(): open(/var/lib/php/sessions/sess_..., O_RDWR) failed: Permission denied, or users are simply logged out at random. This guide covers the usual PHP session errors and their fixes.
Before you start
- Find the active session settings: create a temporary
phpinfo()page and look at the session section (session.save_handler,session.save_path,session.gc_maxlifetime, cookie settings). Delete the page afterwards.
Failed to open stream: Permission denied
PHP cannot write session files to session.save_path. On a VPS, make it writable by the PHP-FPM user:
chown root:www-data /var/lib/php/sessions && chmod 1733 /var/lib/php/sessions(Mode 1733 lets PHP create files but not list other users' sessions.) For a per-site pool, a dedicated folder is cleaner: php_value[session.save_path] = /var/lib/php/sessions/site1 owned by that pool's user. On AlmaLinux/Rocky with SELinux, restore labels with restorecon -Rv on the folder.
No space left on device
A full disk or exhausted inodes stop session writes. Check df -h and df -i; millions of old session files can be the cause themselves.
Old sessions piling up
Debian and Ubuntu disable PHP's probabilistic garbage collection (session.gc_probability = 0) and clean sessions with a systemd timer (phpsessionclean). If you use a custom save path, that cleaner does not know about it. Add your own cron job:
17 * * * * find /var/lib/php/sessions/site1 -type f -mmin +1440 -deleteUsers logged out too early
- Lifetime:
session.gc_maxlifetime(seconds) controls when idle sessions are cleaned. Frameworks often override it (LaravelSESSION_LIFETIME, WordPress uses its own auth cookies). - Another site cleans your sessions: sites sharing one save path with shorter lifetimes delete each other's files. Give each site its own path.
- Cookie domain or path mismatch: switching between www and non-www creates separate cookies. Redirect to one canonical host.
- Secure cookies over HTTP: with
session.cookie_secure = 1, browsers send the cookie only over HTTPS; make sure the whole site uses HTTPS. - SameSite: cross-site flows (payment gateways returning via POST) may need
SameSite=None; Securefor the relevant cookie.
Sessions across multiple servers
Behind a load balancer, file sessions exist only on the server that created them, so users appear logged out when routed elsewhere. Store sessions centrally in Redis:
session.save_handler = redis
session.save_path = "tcp://10.0.0.20:6379?auth=strong-password"This needs the PHP redis extension. Frameworks offer the same through their config (Laravel SESSION_DRIVER=redis).
Session locking slows pages
File sessions are locked while a request runs, so parallel AJAX requests from one user wait for each other. Call session_write_close() as soon as your code no longer needs to write the session.
Headers already sent
session_start(): Session cannot be started after headers have already been sent means output happened before the session started; see the headers already sent guide.
Check the fix
Log in, browse several pages and confirm new session files (or Redis keys) are created and updated, and that the warning no longer appears in the error log.
PHP sessions FAQ
Where are sessions stored on cPanel?
The server manages the path per PHP version; you can change session settings in MultiPHP INI Editor or Select PHP Version options.
Files or Redis for sessions?
Files are fine on one server. Use Redis for multiple servers or very busy sites.
Is it safe to delete session files?
Deleting them logs out the affected users but causes no other damage.
