2026年7月1日
2026年7月1日
ApacheでPHP-FPMと連携する方法|mod_proxy_fcgiの設定と503エラーの確認
Apache で PHP を動かす方法は、Apache に PHP を組み込む mod_php と、PHP を別プロセスの PHP-FPM に任せる方法の2つがあります。現在の主流は後者で、Apache 2.4 の mod_proxy_fcgi を使って PHP のリクエストを PHP-FPM に渡します。この記事では、mod_php から PHP-FPM へ切り替える手順、サイトごとにプールを分ける方法、切り替え後に WordPress で起きやすいつまずき(503 エラーや .htaccess の php_value)とその確認方法を説明します。
なぜ mod_php ではなく PHP-FPM を使うのか
mod_php は Apache のプロセスの中に PHP を読み込むため、画像や CSS を返すだけのプロセスも PHP の分だけメモリを使います。また mod_php はスレッドを使う MPM(event や worker)と組み合わせにくく、prefork MPM に固定されがちです。prefork は1接続に1プロセスを使うので、同時アクセスが増えるとメモリの消費が大きくなります。
PHP-FPM に分けると、Apache は event MPM で静的ファイルと接続の管理に専念し、PHP の実行は PHP-FPM のワーカーが受け持ちます。次のような利点があります。
- Apache を event MPM で動かせるため、同時接続を少ないメモリでさばきやすい
- PHP のワーカー数を Apache とは別に調整できる
- サイトごとに別のプールを作り、実行ユーザーや php.ini の値を分けられる
- PHP のバージョンを Apache と切り離して入れ替えられる
一方で、設定ファイルが Apache 側と PHP-FPM 側の2か所に分かれ、両者をつなぐソケットの権限で詰まりやすくなります。以下ではその点に注意しながら進めます。
作業前の準備
本番サーバーで作業する場合は、事前に設定ファイルのバックアップを取り、可能であればサーバーのスナップショットも作成してください。切り替え中は短時間 PHP が動かない状態になるため、ステージング環境で一度通してから、アクセスの少ない時間帯に本番で行うのが安全です。
# Debian/Ubuntu系
sudo tar czf ~/apache-php-backup-$(date +%F).tar.gz /etc/apache2 /etc/php
# RHEL/AlmaLinux系
sudo tar czf ~/apache-php-backup-$(date +%F).tar.gz /etc/httpd /etc/php-fpm.d /etc/php.ini
現在の構成も確認しておきます。apache2ctl -M(RHEL 系は httpd -M)で読み込み中のモジュールを一覧でき、php_module があれば mod_php、mpm_prefork_module などで MPM が分かります。
sudo apache2ctl -M | grep -E 'php|mpm|proxy'
Debian/Ubuntu系での手順
以下は PHP 8.3 を例にしています。Ubuntu 24.04 の標準パッケージは PHP 8.3 ですが、ディストリビューションのバージョンによって PHP のバージョンは異なります(2026年10月現在)。パッケージ名・サービス名・ソケットのパスの「8.3」は、実際に入っているバージョンに読み替えてください。
1. PHP-FPM をインストールする
sudo apt update
sudo apt install php8.3-fpm
systemctl status php8.3-fpm
WordPress で使う拡張(php8.3-mysql、php8.3-xml、php8.3-mbstring、php8.3-curl、php8.3-gd または php8.3-imagick、php8.3-zip、php8.3-intl など)は、mod_php と PHP-FPM で共通です。すでに入っていれば追加は不要です。ただし php.ini は SAPI ごとに別ファイルで、mod_php は /etc/php/8.3/apache2/php.ini、PHP-FPM は /etc/php/8.3/fpm/php.ini を読みます。upload_max_filesize や memory_limit を変えていた場合は、FPM 側の php.ini にも同じ値を反映してください。ここを忘れると、切り替え後にアップロード上限が戻ったように見えます。
2. Apache のモジュールを切り替える
sudo a2dismod php8.3
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo apache2ctl configtest
sudo systemctl restart apache2
a2enconf php8.3-fpm で有効になる設定ファイルには、.php へのリクエストを PHP-FPM のソケットへ渡す SetHandler が書かれています。中身はおおむね次のような形です。
<FilesMatch ".+\.ph(?:ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
proxy:unix:...|fcgi://localhost の前半がソケットの場所、後半がバックエンドの識別名です。ソケットの代わりに TCP で待ち受ける場合は proxy:fcgi://127.0.0.1:9000 のように書きます。同じサーバー内ならソケットのほうが余計な通信が少なく、外部からつながる心配もありません。
MPM の切り替えで mod_php が残っていると、apache2ctl configtest や再起動の時点で、スレッド型 MPM と組み合わせられない旨のエラーが出ます。その場合は a2dismod で PHP モジュールを外せているか見直してください。
RHEL/AlmaLinux系での手順
RHEL 系の httpd パッケージは、標準で PHP-FPM と組み合わせる構成になっています。php-fpm パッケージを入れると /etc/httpd/conf.d/php.conf に SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" 相当の設定が用意されるため、多くの場合は次の操作で動き始めます。
sudo dnf install php-fpm php-mysqlnd
sudo systemctl enable --now php-fpm
sudo httpd -t
sudo systemctl restart httpd
php.ini は /etc/php.ini、プールの設定は /etc/php-fpm.d/www.conf です。標準のプールの実行ユーザーが apache になっているか、nginx 向けの値になっていないかを確認しておきます。SELinux が有効な環境で、ソケットの場所を変えたりドキュメントルートを独自の場所にしたりした場合は、権限ではなく SELinux のラベルが原因で拒否されることがあります。拒否の記録は ausearch -m avc -ts recent などで確認できます。
動作を確認する
切り替えが済んだら、PHP が PHP-FPM 経由で動いているかを確認します。WordPress の管理画面の「ツール」→「サイトヘルス」→「情報」の「サーバー」欄に、PHP の SAPI として fpm-fcgi と表示されていれば PHP-FPM 経由です(表記は WordPress のバージョンで変わることがあります)。
コマンドで確かめる場合は、一時的に PHP ファイルを置く方法もあります。確認後はすぐに削除してください。phpinfo の出力はサーバーの内部情報を含むため、公開したままにしてはいけません。
<?php
// 確認後すぐ削除すること
echo PHP_SAPI; // fpm-fcgi と表示されれば PHP-FPM 経由
サイトごとにプールを分ける
1台のサーバーで複数の WordPress を動かす場合、サイトごとに PHP-FPM のプールを作り、別のユーザーで実行させると、片方のサイトが改ざんされたときに他方のファイルへ書き込まれる危険を減らせます。
; /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 = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
user は PHP を実行するユーザー、listen.owner と listen.group はソケットファイルの所有者です。ソケットは Apache(Debian 系では www-data)が読み書きできる必要があるため、PHP の実行ユーザーとは分けて指定します。pm.max_children は同時に動く PHP プロセスの上限で、1プロセスあたりのメモリ使用量とサーバーのメモリから決めます。最初は小さめに置き、後述のエラーログを見ながら調整します。
VirtualHost 側では、このサイトだけ専用のソケットへ渡すように書きます。
<VirtualHost *:443>
ServerName site1.example.com
DocumentRoot /var/www/site1
<FilesMatch ".+\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm-site1.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
プールの設定は sudo php-fpm8.3 -t で文法を確認してから sudo systemctl reload php8.3-fpm で反映します。ドキュメントルートのファイルは、PHP の実行ユーザー(ここでは site1)が読めて、アップロード用の wp-content/uploads だけ書き込める状態にするのが基本です。
切り替え後につまずきやすい点
503 Service Unavailable になる
PHP のページだけが 503 になる場合は、Apache が PHP-FPM のソケットに接続できていません。Apache のエラーログ(Debian 系は /var/log/apache2/error.log、RHEL 系は /var/log/httpd/error_log)を見ると、ソケットへの接続に失敗した旨のメッセージが記録されています。可能性の高い順に確認します。
- PHP-FPM が起動していない:
systemctl status php8.3-fpmで確認し、設定の誤りで起動に失敗していないかをjournalctl -u php8.3-fpmで見ます。 - ソケットのパスが一致していない:
ls -l /run/php/で実際のソケット名を確認し、SetHandlerのパスと見比べます。PHP のバージョンを上げたあとに古いパスのまま残っている例がよくあります。 - ソケットの権限が足りない:エラーログに Permission denied が出ていれば、
listen.owner・listen.group・listen.modeを見直します。
.htaccess の php_value で 500 エラーになる
mod_php の時代に、.htaccess へ php_value upload_max_filesize 64M のような行を書いていたサイトは、PHP-FPM に切り替えると 500 エラーになります。php_value は mod_php が提供する命令なので、mod_php を外すと Apache が解釈できない命令として扱うためです。エラーログには Invalid command 'php_value' のようなメッセージが出ます。
この行は削除し、値はプールの設定(php_admin_value や php_value)か、ドキュメントルートに置く .user.ini に移します。.user.ini は PHP-FPM で読み込まれますが、既定では数分ごとにしか再読み込みされないため、変更がすぐ反映されなくても慌てずに待つか、PHP-FPM を reload してください。
コードを変えても反映されない
PHP-FPM では OPcache が FPM のプロセス単位で保持されます。設定によってはファイルを更新しても古いキャッシュが使われ続けることがあり、その場合は systemctl reload php8.3-fpm で解消します。Apache の再起動では PHP-FPM 側のキャッシュは消えない点に注意してください。
処理の重いページでタイムアウトする
PHP-FPM と組み合わせると、PHP の max_execution_time に加えて、Apache のプロキシ側の待ち時間(Timeout や ProxyTimeout)と PHP-FPM の request_terminate_timeout が関わります。インポートなど時間のかかる処理が途中で切れる場合は、どの段階のタイムアウトかをエラーログで切り分けてから値を調整します。
まとめ
- Apache と PHP-FPM は mod_proxy_fcgi の
SetHandler "proxy:unix:ソケット|fcgi://localhost"でつなぐ - Debian/Ubuntu 系は mod_php と prefork を外し、event MPM・proxy_fcgi・php-fpm 用の conf を有効にする。RHEL 系は php-fpm を入れて起動すれば標準で連携する
- php.ini は SAPI ごとに別ファイル。FPM 側の php.ini やプール設定に値を移す
- 複数サイトはプールを分け、実行ユーザーとソケットを別にする
- 503 はソケットの起動・パス・権限、500 は .htaccess の php_value を最初に疑う
- 作業前にバックアップを取り、ステージングで確認してから本番に適用する