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_pathdirectory owned by root, PHP-FPM runs as www-data/tmpmounted withnoexecornosuidoption- 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.Check current session save path and permissions:
- 2.```bash
- 3.# Find the session save path
- 4.php -r "echo ini_get('session.save_path');"
- 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.Fix directory permissions:
- 2.```bash
- 3.# Change ownership to PHP-FPM user
- 4.sudo chown -R www-data:www-data /var/lib/php/sessions
- 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.Configure custom session save path:
- 2.```php
- 3.// In PHP code (before session_start)
- 4.$sessionPath = '/var/www/html/storage/sessions';
- 5.if (!is_dir($sessionPath)) {
- 6.mkdir($sessionPath, 0770, true);
- 7.}
- 8.session_save_path($sessionPath);
- 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.Fix SELinux context (if applicable):
- 2.```bash
- 3.# Check if SELinux is blocking
- 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.Use database or Redis session handler:
- 2.```php
- 3.// Redis session handler (more scalable)
- 4.// Requires: pecl install redis
- 5.ini_set('session.save_handler', 'redis');
- 6.ini_set('session.save_path', 'tcp://127.0.0.1:6379');
- 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_pathin 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