2026年5月23日

2026年5月23日

PHP-FPMのインストールと設定方法:サイトごとにプールを分けて実行ユーザーを分離する

1台のサーバーで複数の WordPress を動かすなら、PHP-FPM のプールをサイトごとに作り、それぞれ別の Linux ユーザーで動かす構成がおすすめです。あるサイトのプラグインが乗っ取られても、ほかのサイトのファイルを読み書きされにくくなり、メモリの上限やログもサイト単位で管理できます。この記事では、Ubuntu を例に、PHP-FPM のインストールからサイト専用ユーザーとディレクトリの準備、プール設定、Nginx との接続、動作確認とつまずきやすい点までを説明します。既定のプール(www.conf)1つで動かす基本の設定は別記事で扱っているので、ここではプールを分割する手順に絞ります。

プールを分けると何が変わるか

Ubuntu の PHP-FPM は、インストール直後に www というプールが1つだけあり、www-data ユーザーで動きます。この状態で複数のサイトを載せると、すべてのサイトの PHP が同じユーザーで実行されます。つまり、サイト A の脆弱なプラグインから送り込まれたコードが、サイト B の wp-config.php を読めてしまう可能性があります。

サイトごとにプールを分けると、次のことができるようになります。

  • 実行ユーザーの分離: サイト A の PHP はサイト A のファイルにだけ書き込めるようにできます。
  • リソースの上限: プールごとに pm.max_children を決められるので、1つのサイトへのアクセス集中がほかのサイトのワーカーを使い切る事態を防げます。
  • PHP 設定の個別化: memory_limit やアップロードの上限、エラーログの出力先をサイトごとに変えられます。
  • 原因の切り分け: スローログやエラーログがサイト単位に分かれるため、重いサイトや問題のあるサイトを特定しやすくなります。

作業は設定ファイルとファイルの所有者を変更するので、事前にサーバーのスナップショットか、/etc/php・/etc/nginx とサイトのファイルのバックアップを取ってください。稼働中のサイトに適用する前に、ステージング環境で同じ手順を試すと安全です。

PHP-FPM のインストール

Ubuntu 24.04 の標準リポジトリでは PHP 8.3 が提供されています(2026年10月現在。OS の版によって異なります)。以下は PHP 8.3 の例なので、手元のバージョンに読み替えてください。

sudo apt update
sudo apt install php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd \
  php8.3-mbstring php8.3-xml php8.3-zip php8.3-intl
systemctl status php8.3-fpm

AlmaLinux や Rocky Linux などの RHEL 系では、パッケージ名やプール設定の場所(/etc/php-fpm.d/)、サービス名が異なります。考え方は同じなので、パスを読み替えて進めてください。

サイト専用のユーザーとディレクトリを用意する

ここでは site1 というサイトを例にします。まずログインできないシステムユーザーを作ります。

sudo useradd --system --no-create-home --shell /usr/sbin/nologin site1

次に、ディレクトリを用途ごとに分けます。Web サーバーが公開するのは public だけにし、一時ファイルとログは公開ディレクトリの外に置きます。

sudo mkdir -p /var/www/site1/{public,tmp,logs}
sudo chown -R site1:site1 /var/www/site1
sudo find /var/www/site1 -type d -exec chmod 750 {} \;
sudo find /var/www/site1 -type f -exec chmod 640 {} \;

# Nginx(www-data)が静的ファイルを読めるよう、site1 グループに加える
sudo usermod -aG site1 www-data

所有者を site1、グループを site1 にし、Nginx の実行ユーザーである www-data をそのグループに加えています。こうすると、PHP(site1 として実行)はファイルを書き込め、Nginx は画像や CSS を読むだけ、という役割分担になります。ほかのサイトのユーザーは、このディレクトリに入れません。グループへの追加は、Nginx を再起動したあとに反映されます。

WordPress のファイルを置いたあとにも、同じ chown と chmod をもう一度実行して所有者とパーミッションをそろえます。wp-config.php は PHP だけが読めればよいので、600 にしておくとより安全です。

sudo chmod 600 /var/www/site1/public/wp-config.php

この構成には、WordPress の更新が楽になる利点もあります。WordPress は、PHP の実行ユーザーとファイルの所有者が一致していると、管理画面からのプラグイン更新でファイルを直接書き込みます。一致していないと、更新のたびに FTP の接続情報を求める画面が表示されます。

プールの設定ファイルを作る

/etc/php/8.3/fpm/pool.d/ に、サイトごとの設定ファイルを作ります。このディレクトリにある .conf で終わるファイルは、すべて読み込まれます。

; /etc/php/8.3/fpm/pool.d/site1.conf
[site1]
user = site1
group = site1

listen = /run/php/php8.3-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 6
pm.process_idle_timeout = 30s
pm.max_requests = 500

slowlog = /var/www/site1/logs/php-slow.log
request_slowlog_timeout = 5s
request_terminate_timeout = 120s

php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 32M
php_admin_value[post_max_size] = 32M
php_admin_value[upload_tmp_dir] = /var/www/site1/tmp
php_admin_value[sys_temp_dir] = /var/www/site1/tmp
php_admin_value[session.save_path] = /var/www/site1/tmp
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/www/site1/logs/php-error.log
php_admin_value[open_basedir] = /var/www/site1/public:/var/www/site1/tmp

項目ごとの意図を説明します。

user・group と listen

user と group がこのプールのワーカーを動かすユーザーです。listen のソケットはサイトごとに別の名前にします。ソケットの所有者は Nginx の実行ユーザーにしておき、Nginx だけがこのソケットに接続できるようにします。

pm の方式と上限

アクセスの少ないサイトを多数載せるサーバーでは、リクエストがあるときだけワーカーを起動する ondemand が向いています。pm.process_idle_timeout の時間だけ使われなかったワーカーは終了するため、待機中のメモリを抑えられます。アクセスの多いサイトだけ dynamic にする、といった混在もできます。

php_admin_value

php_admin_value と php_admin_flag で指定した値は、WordPress やプラグインのコードの ini_set() で上書きできません。上書きを許してよい値は php_value を使います。一時ファイルとセッションの保存先をサイト専用にしておくと、共有の /tmp を介してほかのサイトのファイルにアクセスされる経路を減らせます。なお、Ubuntu・Debian の PHP パッケージは、古いセッションファイルの削除を PHP 自身ではなく標準の保存先を対象にした定期処理で行う設定になっています。WordPress 本体は PHP のセッションを使いませんが、セッションを使うプラグインがある場合は、tmp に古いファイルがたまっていないか時々確認してください。

open_basedir の扱い

open_basedir は、PHP が開けるファイルの場所を制限する設定です。分離をより強められますが、プラグインによっては指定外の場所(たとえば /usr/share 配下のフォントや証明書)を読もうとしてエラーになることがあります。導入するときはエラーログを見ながら、必要なパスだけを追加してください。うまくいかない場合は、まず open_basedir なしでユーザー分離だけを先に済ませるのも現実的な選択です。

既定の www プールの扱い

既定の www プールを使わなくなったら、ファイル名を変えて読み込まれないようにします。末尾が .conf でなければ読み込まれません。

sudo mv /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf.disabled

ほかのアプリケーションが php8.3-fpm.sock を使っていないか、先に Nginx の設定を検索して確認してください。

Nginx からサイト専用のソケットに接続する

サーバーブロックごとに、公開ディレクトリと接続先のソケットを指定します。

server {
    listen 80;
    server_name site1.example.com;
    root /var/www/site1/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock;
    }
}

fastcgi_pass のソケット名は、プール設定の listen と一致させます。サイトを増やすたびに、ユーザー、ディレクトリ、プール、サーバーブロックの4点を1組で作ると考えると漏れが減ります。

反映と動作確認

設定の構文を確認してから反映します。

sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
sudo nginx -t
sudo systemctl restart nginx   # www-data のグループ追加を反映するため再起動

サイトにアクセスしたあと、ワーカーがどのユーザーで動いているかを確認します。

ps -eo user,pid,args | grep "php-fpm: pool"

site1 ユーザーで php-fpm: pool site1 が動いていれば成功です。ondemand の場合、アクセスがないとワーカーは表示されません。

メモリの見積もり

プールを分けても、サーバーのメモリは共有です。各プールの pm.max_children を合計した数のワーカーが同時に動いても、メモリが足りるかを確認します。目安は「PHP に割り当てられるメモリ ÷ ワーカー1つの平均メモリ」が、全プールの pm.max_children の合計を下回らないことです。ワーカー1つのメモリは WordPress のプラグイン構成で大きく変わるため、実際に動いているワーカーの使用量を ps や top で測ってから決めてください。すべてのサイトが同時に上限まで使うことは少ないものの、上限の合計がメモリを大きく超える設定は、アクセス集中時のスワップや強制終了の原因になります。

つまずきやすい点

  • 502 Bad Gateway: ソケット名の食い違いか、ソケットの権限不足が多い原因です。Nginx のエラーログに出るソケットのパスと、ls -l /run/php/ の結果を見比べます。
  • アップロードやプラグインの更新に失敗する: tmp ディレクトリが存在しない、または site1 が書き込めない状態です。所有者とパーミッションを確認します。
  • 「open_basedir restriction in effect」という警告: プラグインが許可外のパスを読もうとしています。エラーログに出たパスが本当に必要か確認したうえで、open_basedir に追加します。
  • ログが出力されない: エラーログはワーカー(site1)の権限で書き込まれるため、logs ディレクトリに site1 の書き込み権限が必要です。
  • WP-CLI で権限エラーになる: コマンドラインの PHP は PHP-FPM のプール設定を使いません。sudo -u site1 wp plugin list のように、サイトのユーザーとして実行します。

まとめ

  • 複数サイトを載せるサーバーでは、サイトごとに Linux ユーザーと PHP-FPM のプールを作り、実行ユーザーを分離します。
  • 公開ディレクトリ、一時ファイル、ログを分け、所有者をサイトのユーザーにし、Nginx はグループで読むだけにします。
  • php_admin_value でメモリやアップロード、一時ファイルの場所をサイト単位で固定し、open_basedir はログを見ながら段階的に導入します。
  • 各プールの pm.max_children の合計がサーバーのメモリに収まるよう見積もります。
  • php-fpm8.3 -t と nginx -t で構文を確認し、ps でワーカーのユーザーを確かめます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲