Fix PHP Warning: session_start(): Failed to read session data: files

intermediate๐Ÿ˜ PHP2026-04-02| PHP 7.x / 8.x on Linux (Ubuntu, Debian, CentOS), Apache/Nginx + PHP-FPM

Error Message

Warning: session_start(): Failed to read session data: files
#php#session#permission#tmp

The Error

Your app is throwing this in the logs:

Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions)
Warning: session_start(): open(SESSION_PATH/sess_abc123, O_RDWR) failed: Permission denied (13)

Sometimes it comes paired with:

Warning: session_start(): Failed to read session data: files (path: /tmp)

Users are getting logged out randomly, shopping carts are being wiped, login forms don't stick. Classic session failure at the worst possible time.

Root Cause

PHP writes session data to disk. The process running PHP โ€” usually www-data, apache, or nginx โ€” doesn't have write permission on the session save path, or the directory doesn't exist at all.

Three things break this:

  • Wrong ownership on the session directory (most common by far)
  • Wrong permissions โ€” the directory exists but PHP's user can't write to it
  • Wrong path in php.ini pointing somewhere that doesn't exist

On Ubuntu/Debian, PHP-FPM pools run as www-data. But after a distro upgrade or manual package reinstall, the session directory often gets reset to root ownership โ€” and suddenly nothing works.

Step 1 โ€” Find Your Session Path

Check where PHP is actually trying to save sessions:

php -r "echo session_save_path();"

Or from a PHP file:

<?php echo ini_get('session.save_path'); ?>

Also check your php.ini:

php --ini
grep -r 'session.save_path' /etc/php/

Common paths to check:

  • /var/lib/php/sessions (Ubuntu/Debian default)
  • /tmp
  • /var/lib/php/session (CentOS/RHEL)
  • A custom path set in your pool config or php.ini

Step 2 โ€” Check Ownership and Permissions

ls -la /var/lib/php/sessions

You'll probably see something like:

drwx-wx-wt 2 root root 4096 Apr  1 02:14 sessions

Check which user PHP-FPM is running as:

ps aux | grep php-fpm | grep -v grep

Or check the pool config directly:

grep -E '^user|^group' /etc/php/8.*/fpm/pool.d/www.conf

Fix 1 โ€” Fix Ownership (Most Common Fix)

If your PHP-FPM runs as www-data:

sudo chown root:www-data /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions

On CentOS/RHEL where PHP-FPM runs as apache:

sudo chown root:apache /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions

Ubuntu sets the sticky bit (+t) by default โ€” keep it. It stops one user from deleting another user's session files:

sudo chmod 1770 /var/lib/php/sessions

Fix 2 โ€” Directory Doesn't Exist

Package updates occasionally wipe the directory entirely. Recreate it:

sudo mkdir -p /var/lib/php/sessions
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 1770 /var/lib/php/sessions

Fix 3 โ€” Use a Per-Application Session Path

Can't touch system directories? Running multiple apps under different users? Set a custom session path per application instead:

<?php
$sessionDir = __DIR__ . '/../storage/sessions';
if (!is_dir($sessionDir)) {
    mkdir($sessionDir, 0700, true);
}
session_save_path($sessionDir);
session_start();

Keep the directory outside the web root โ€” it must not be reachable via HTTP. Then confirm it's writable:

ls -la storage/sessions/

Fix 4 โ€” PHP-FPM Pool-Level Config

Rather than touching php.ini globally, set the session path inside the pool config. This keeps sessions isolated per virtual host:

# /etc/php/8.2/fpm/pool.d/myapp.conf
[myapp]
user = www-data
group = www-data
php_value[session.save_path] = /var/lib/php/sessions/myapp

Then create and permission that directory:

sudo mkdir -p /var/lib/php/sessions/myapp
sudo chown www-data:www-data /var/lib/php/sessions/myapp
sudo chmod 700 /var/lib/php/sessions/myapp
sudo systemctl restart php8.2-fpm

Fix 5 โ€” SELinux Is Blocking Writes (CentOS/RHEL)

Permissions look correct but sessions still fail on CentOS/RHEL? SELinux might be silently denying writes. Check the audit log:

sudo ausearch -m avc -ts recent | grep php

If you see denials, restore the correct SELinux context:

sudo restorecon -R /var/lib/php/sessions

Or switch to permissive mode temporarily to confirm SELinux is the culprit:

sudo setenforce 0
# test sessions
sudo setenforce 1

Verify the Fix

Time to confirm it actually works. Run this test script right after applying the fix:

<?php
session_start();
$_SESSION['test'] = 'ok';
echo 'Session ID: ' . session_id() . PHP_EOL;
echo 'Save path: ' . session_save_path() . PHP_EOL;
echo 'Status: ' . (session_status() === PHP_SESSION_ACTIVE ? 'ACTIVE' : 'FAILED');

Then check that a session file was actually created:

ls -la /var/lib/php/sessions/

You should see files like sess_abc123xyz with recent timestamps. An empty directory after running the test means the write is still failing.

Watch the PHP error log in real time while testing:

sudo tail -f /var/log/php8.2-fpm.log
# or
sudo journalctl -u php8.2-fpm -f

Tips

Not sure what chmod 1770 or 770 actually grants at 2 AM? The Unix Permissions Calculator on ToolCraft lets you visualize permission bits instantly โ€” before you accidentally open up too much access or lock PHP out entirely.

Prevention

  • Add a healthcheck: A cron job running every 5 minutes that verifies the session directory exists and is writable โ€” and alerts you โ€” will catch this before users do.
  • Lock down the path in php.ini: Don't rely on the default. Explicitly set session.save_path so it never silently changes after a package upgrade.
  • Use Redis or Memcached for sessions on sites handling hundreds of concurrent users: session.save_handler = redis with session.save_path = "tcp://127.0.0.1:6379" eliminates filesystem permission issues entirely.
  • Monitor session errors: Set up log alerts for session_start(): Failed so you catch this before users report it.
  • After OS upgrades: Always run ls -la /var/lib/php/sessions โ€” package updates occasionally reset ownership back to root.

Related Error Notes