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#セッション#パーミッション#tmp

エラーの内容

アプリのログに以下のエラーが出力されています:

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-dataapache、または 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 1770770 が実際に何を許可するか自信がない場合、ToolCraft の Unix パーミッション計算ツールを使えば、パーミッションビットを即座に視覚化できます — 誤ってアクセスを開放しすぎたり、PHPを完全に締め出したりする前に確認できます。

予防策

  • ヘルスチェックを追加する: 5分ごとに動作するcronジョブでセッションディレクトリの存在と書き込み可否を確認し、問題があればアラートを送るようにすれば、ユーザーより先に問題を検知できます。
  • php.ini でパスを明示的に設定する: デフォルト設定に依存しないでください。session.save_path を明示的に設定することで、パッケージアップグレード後に暗黙的に変更されることを防げます。
  • 大量の同時接続を処理するサイトでは Redis や Memcached をセッションに使用する: session.save_handler = redissession.save_path = "tcp://127.0.0.1:6379" の設定により、ファイルシステムのパーミッション問題を根本から排除できます。
  • セッションエラーを監視する: session_start(): Failed のログアラートを設定して、ユーザーから報告される前に問題を検知できるようにしましょう。
  • OSアップグレード後: 必ず ls -la /var/lib/php/sessions を実行してください — パッケージの更新で所有者が root にリセットされることがあります。

Related Error Notes