エラーの内容
アプリのログに以下のエラーが出力されています:
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)
以下のエラーと同時に発生することもあります:
Warning: session_start(): Failed to read session data: files (path: /tmp)
ユーザーが突然ログアウトされる、ショッピングカートの中身が消える、ログインフォームの状態が保持されない。最悪のタイミングで起きる、典型的なセッション障害です。
原因
PHPはセッションデータをディスクに書き込みます。PHPを実行しているプロセス — 通常は www-data、apache、または nginx — が、セッション保存パスへの書き込み権限を持っていないか、そもそもディレクトリが存在しないことが原因です。
主に3つの原因が考えられます:
- オーナーシップが間違っている — セッションディレクトリの所有者設定ミス(最も多い原因)
- パーミッションが間違っている — ディレクトリは存在するが、PHPのユーザーが書き込めない
- パスが間違っている —
php.iniで存在しないパスを指定している
Ubuntu/Debian では、PHP-FPM のプールは www-data として動作します。しかし、ディストリビューションのアップグレードや手動でのパッケージ再インストール後に、セッションディレクトリの所有者が root にリセットされることがあり、突然何も動かなくなります。
ステップ1 — セッションパスを確認する
PHPが実際にセッションを保存しようとしているパスを確認します:
php -r "echo session_save_path();"
またはPHPファイルから確認する場合:
<?php echo ini_get('session.save_path'); ?>
php.ini も確認しましょう:
php --ini
grep -r 'session.save_path' /etc/php/
よく使われるパス:
/var/lib/php/sessions(Ubuntu/Debian のデフォルト)/tmp/var/lib/php/session(CentOS/RHEL)- プール設定や
php.iniでカスタム指定したパス
ステップ2 — 所有者とパーミッションを確認する
ls -la /var/lib/php/sessions
おそらく以下のような出力が表示されます:
drwx-wx-wt 2 root root 4096 Apr 1 02:14 sessions
PHP-FPM がどのユーザーで動作しているか確認します:
ps aux | grep php-fpm | grep -v grep
またはプール設定ファイルから直接確認します:
grep -E '^user|^group' /etc/php/8.*/fpm/pool.d/www.conf
修正1 — 所有者を修正する(最も一般的な修正方法)
PHP-FPM が www-data で動作している場合:
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions
PHP-FPM が apache で動作している CentOS/RHEL の場合:
sudo chown root:apache /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions
Ubuntu ではデフォルトでスティッキービット(+t)が設定されています — これは維持してください。あるユーザーが別のユーザーのセッションファイルを削除できないようにするためです:
sudo chmod 1770 /var/lib/php/sessions
修正2 — ディレクトリが存在しない場合
パッケージの更新でディレクトリが完全に削除されることがあります。再作成してください:
sudo mkdir -p /var/lib/php/sessions
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 1770 /var/lib/php/sessions
修正3 — アプリケーションごとにセッションパスを設定する
システムディレクトリに触れない、または複数のアプリを異なるユーザーで動かしている場合は、アプリケーションごとにカスタムセッションパスを設定しましょう:
<?php
$sessionDir = __DIR__ . '/../storage/sessions';
if (!is_dir($sessionDir)) {
mkdir($sessionDir, 0700, true);
}
session_save_path($sessionDir);
session_start();
ディレクトリはWebルートの外に配置してください — HTTPでアクセスできてはいけません。書き込み可能かどうかも確認しましょう:
ls -la storage/sessions/
修正4 — PHP-FPM プールレベルの設定
php.ini をグローバルに変更する代わりに、プール設定ファイル内でセッションパスを設定します。これにより、バーチャルホストごとにセッションを分離できます:
# /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
次に、そのディレクトリを作成してパーミッションを設定します:
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
修正5 — SELinux が書き込みをブロックしている場合(CentOS/RHEL)
パーミッションは正しく見えるのに CentOS/RHEL でセッションが失敗する場合、SELinux がサイレントに書き込みを拒否している可能性があります。監査ログを確認してください:
sudo ausearch -m avc -ts recent | grep php
拒否ログが確認できた場合、正しい SELinux コンテキストを復元します:
sudo restorecon -R /var/lib/php/sessions
または、SELinux が原因かどうか確認するため、一時的にパーミッシブモードに切り替えてテストします:
sudo setenforce 0
# セッションをテストする
sudo setenforce 1
修正を確認する
修正が実際に機能しているか確認しましょう。修正を適用した直後に、以下のテストスクリプトを実行してください:
<?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');
セッションファイルが実際に作成されているか確認します:
ls -la /var/lib/php/sessions/
最近のタイムスタンプを持つ sess_abc123xyz のようなファイルが表示されるはずです。テスト実行後もディレクトリが空の場合は、まだ書き込みに失敗しています。
テスト中はPHPエラーログをリアルタイムで監視しましょう:
sudo tail -f /var/log/php8.2-fpm.log
# または
sudo journalctl -u php8.2-fpm -f
補足
深夜2時に chmod 1770 や 770 が実際に何を許可するか自信がない場合、ToolCraft の Unix パーミッション計算ツールを使えば、パーミッションビットを即座に視覚化できます — 誤ってアクセスを開放しすぎたり、PHPを完全に締め出したりする前に確認できます。
予防策
- ヘルスチェックを追加する: 5分ごとに動作するcronジョブでセッションディレクトリの存在と書き込み可否を確認し、問題があればアラートを送るようにすれば、ユーザーより先に問題を検知できます。
- php.ini でパスを明示的に設定する: デフォルト設定に依存しないでください。
session.save_pathを明示的に設定することで、パッケージアップグレード後に暗黙的に変更されることを防げます。 - 大量の同時接続を処理するサイトでは Redis や Memcached をセッションに使用する:
session.save_handler = redisとsession.save_path = "tcp://127.0.0.1:6379"の設定により、ファイルシステムのパーミッション問題を根本から排除できます。 - セッションエラーを監視する:
session_start(): Failedのログアラートを設定して、ユーザーから報告される前に問題を検知できるようにしましょう。 - OSアップグレード後: 必ず
ls -la /var/lib/php/sessionsを実行してください — パッケージの更新で所有者が root にリセットされることがあります。

