Introduction

PHP stores session data as files in the directory specified by session.save_path (default: /tmp or /var/lib/php/sessions). If the PHP-FPM or Apache process user does not have write permissions to this directory, session creation fails. Users cannot log in, shopping carts are empty on page refresh, and session-dependent features break silently.

Symptoms

  • Warning: session_start(): open(/tmp/sess_abc123, O_RDWR) failed: Permission denied (13)
  • Warning: session_start(): Failed to read session data: files
  • Users cannot stay logged in - session data not persisted
  • Session cookie is set but server cannot write session file
  • Works from CLI but fails from web server

``` Warning: session_start(): open(/var/lib/php/sessions/sess_7f3a2b1c4d5e, O_RDWR) failed: Permission denied (13) in /var/www/html/login.php on line 10

Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/html/login.php on line 10 ```

Common Causes

  • session.save_path directory owned by root, PHP-FPM runs as www-data
  • /tmp mounted with noexec or nosuid option
  • Directory permissions changed by system update or cleanup script
  • SELinux blocking PHP-FPM from writing to session directory
  • Docker container with incorrect volume mount permissions

Step-by-Step Fix

  1. 1.Check current session save path and permissions:
  2. 2.```bash
  3. 3.# Find the session save path
  4. 4.php -r "echo ini_get('session.save_path');"
  5. 5.# Output: /var/lib/php/sessions

# Check directory ownership and permissions ls -la /var/lib/php/ # drwxr-x--- 2 root www-data 4096 Jan 15 10:30 sessions

# Check which user PHP-FPM runs as ps aux | grep php-fpm # www-data user

# Verify the user can write to the directory sudo -u www-data touch /var/lib/php/sessions/test # Permission denied? That's the problem. ```

  1. 1.Fix directory permissions:
  2. 2.```bash
  3. 3.# Change ownership to PHP-FPM user
  4. 4.sudo chown -R www-data:www-data /var/lib/php/sessions
  5. 5.sudo chmod 770 /var/lib/php/sessions

# Or add www-data to the group that owns the directory sudo usermod -a -G root www-data # Not recommended for security sudo chmod 775 /var/lib/php/sessions

# Verify sudo -u www-data touch /var/lib/php/sessions/test rm /var/lib/php/sessions/test ```

  1. 1.Configure custom session save path:
  2. 2.```php
  3. 3.// In PHP code (before session_start)
  4. 4.$sessionPath = '/var/www/html/storage/sessions';
  5. 5.if (!is_dir($sessionPath)) {
  6. 6.mkdir($sessionPath, 0770, true);
  7. 7.}
  8. 8.session_save_path($sessionPath);
  9. 9.session_start();

// Or in php.ini // session.save_path = "/var/www/html/storage/sessions"

// Or in PHP-FPM pool config // /etc/php/8.2/fpm/pool.d/www.conf php_admin_value[session.save_path] = /var/www/html/storage/sessions ```

  1. 1.Fix SELinux context (if applicable):
  2. 2.```bash
  3. 3.# Check if SELinux is blocking
  4. 4.getenforce

# Check audit log for denials sudo ausearch -m avc -ts recent | grep php

# Set correct SELinux context for session directory sudo chcon -R -t httpd_sys_rw_content_t /var/lib/php/sessions

# Make it persistent sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/lib/php/sessions(/.*)?" sudo restorecon -Rv /var/lib/php/sessions ```

  1. 1.Use database or Redis session handler:
  2. 2.```php
  3. 3.// Redis session handler (more scalable)
  4. 4.// Requires: pecl install redis
  5. 5.ini_set('session.save_handler', 'redis');
  6. 6.ini_set('session.save_path', 'tcp://127.0.0.1:6379');
  7. 7.session_start();

// Database session handler // In php.ini or code: ini_set('session.save_handler', 'user');

// Or use Symfony session handler use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; $handler = new PdoSessionHandler($pdo); $session->registerHandler($handler); ```

Prevention

  • Set session.save_path in PHP-FPM pool config, not just php.ini
  • Ensure the directory is writable by the PHP-FPM user
  • Add a session health check to your monitoring:
  • ```php
  • // health-check.php
  • session_start();
  • $_SESSION['health_check'] = time();
  • session_write_close();
  • // If this returns 200, sessions are working
  • http_response_code(200);
  • echo "OK";
  • `
  • Clean up old session files periodically:
  • ```bash
  • # Cron: remove session files older than 24 hours
  • find /var/lib/php/sessions -name "sess_*" -mtime +1 -delete
  • `
  • Consider Redis or database session storage for multi-server deployments