2026年9月10日
2026年9月10日
NginxとApacheの組み合わせ構成(Nginxを前段に置くWordPressの設定と注意点)
Nginx と Apache を組み合わせるときは、外からの 80/443 番を Nginx が受けて TLS の処理と静的ファイルの配信を担当し、PHP の実行と .htaccess の解釈は内部ポートで待ち受ける Apache に任せる「Nginx 前段・Apache 後段」の構成が一般的です。この記事では、この構成を選ぶべき場面と選ばなくてよい場面、WordPress で動かすための Apache と Nginx の設定、そして導入後によく起きるリダイレクトのループや送信元 IP の取り違えへの対処を説明します。
この構成が向いている場面・向いていない場面
Nginx と Apache を重ねると、設定ファイルもログも2系統になり、トラブルの切り分けに手間がかかります。導入の前に、本当に両方が必要かを確認しておきます。
向いているのは次のような場面です。
.htaccessに書かれたリダイレクトやアクセス制限、プラグインが自動で書き込むルールをそのまま活かしたい。- 既存の Apache 環境を大きく変えずに、TLS の終端や静的ファイルの配信、キャッシュだけを Nginx に任せたい。
- 同じサーバーで、Apache 前提のアプリと Nginx 向きのアプリを並べて動かしたい。
一方、新しく WordPress のサーバーを用意するなら、Nginx と PHP-FPM だけの構成、あるいは Apache の event MPM と PHP-FPM の構成でも十分な性能が出ます。どちらも1系統で済むため運用は楽です。Nginx は .htaccess を読まないので、Nginx 単体にする場合はパーマリンクやセキュリティ系プラグインのルールを Nginx の設定に書き直す必要があり、その手間を避けたいときに組み合わせ構成が候補になる、という位置づけです。
全体の流れと役割分担
リクエストの流れは「利用者 → Nginx(80/443)→ Apache(127.0.0.1:8080)→ PHP → WordPress」です。役割は次のように分けます。
- Nginx: TLS の終端、HTTP/2、画像・CSS・JavaScript などの静的ファイルの配信、必要に応じてキャッシュ。
- Apache: PHP の実行(mod_php または PHP-FPM 経由)と
.htaccessの処理。
ここでは Debian / Ubuntu のパッケージ版を例にします。RHEL 系ではファイルの場所やコマンドが異なるので読み替えてください。作業前に /etc/nginx と /etc/apache2 のバックアップを取り、可能ならステージング環境で試してから本番に適用します。
ステップ1:Apacheを内部ポートに移す
Apache が 80 番を使ったままだと Nginx が起動できません。Apache の待ち受けを、外から届かない 127.0.0.1:8080 に移します。
# /etc/apache2/ports.conf
Listen 127.0.0.1:8080
# /etc/apache2/sites-available/example.conf
<VirtualHost 127.0.0.1:8080>
ServerName example.com
DocumentRoot /var/www/example
<Directory /var/www/example>
AllowOverride All
Require all granted
</Directory>
# Nginx から渡された送信元 IP を使う
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" proxied
CustomLog ${APACHE_LOG_DIR}/example_access.log proxied
ErrorLog ${APACHE_LOG_DIR}/example_error.log
</VirtualHost>
sudo a2enmod remoteip rewrite
sudo apache2ctl configtest
ports.conf に 443 番の Listen が残っている場合は、TLS を Nginx 側に移すのでコメントアウトします。
mod_remoteip は Apache 2.4 に含まれるモジュールで、X-Forwarded-For に入った元の IP を Apache 内部の送信元 IP として扱わせます。これを入れないと、ログにも PHP にも送信元が 127.0.0.1 と記録されます。ログ書式で %h ではなく %a を使っているのも同じ理由です。以前よく使われた mod_rpaf は、Apache 2.4 では mod_remoteip で代替できます。
RemoteIPInternalProxy には、信頼するプロキシ(ここでは同じサーバーの Nginx)だけを指定します。範囲を広げすぎると、外部から偽の X-Forwarded-For を送られたときにそのまま信じてしまいます。
ステップ2:Nginxを前段に置く
Nginx は 80/443 番で受け、静的ファイルは自分で返し、それ以外を Apache に転送します。
# /etc/nginx/sites-available/example.conf
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/example;
client_max_body_size 64m;
# .htaccess などを外から読ませない
location ~ /\.ht {
deny all;
}
# 静的ファイルは Nginx が直接返し、無ければ Apache へ
location ~* \.(?:css|js|jpe?g|png|gif|webp|avif|svg|ico|woff2?)$ {
try_files $uri @apache;
expires 30d;
access_log off;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
location @apache {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
sudo nginx -t
sudo systemctl restart apache2
sudo systemctl reload nginx
ポイントは次のとおりです。
- Nginx の
rootと Apache のDocumentRootを同じディレクトリにそろえます。ずれていると静的ファイルが 404 になります。 Hostヘッダーを渡さないと、Apache がどの VirtualHost で処理すべきか判断できず、別のサイトが表示されることがあります。client_max_body_sizeは Nginx の既定値が小さいため、メディアのアップロードやプラグインのインストールで 413 エラーにならないよう、PHP のupload_max_filesizeと釣り合う値にします。
静的ファイルを Nginx が直接返すということは、その部分では .htaccess のルールが働かないということでもあります。たとえば wp-content/uploads に置いた .htaccess で特定のファイルへのアクセスを禁止しているプラグインがある場合、同じ制限を Nginx 側にも書かないと保護が外れます。静的ファイルの拡張子を広げるときは、この点を確認してください。
最後に Certbot などで Nginx に証明書を設定し、HTTPS 化します(sudo certbot --nginx -d example.com)。
ステップ3:WordPressにHTTPSを正しく伝える
TLS を Nginx で終端すると、Apache と PHP から見た通信は HTTP になります。このままだと WordPress は「HTTP でアクセスされた」と判断して HTTPS の URL へリダイレクトし続け、ブラウザに「リダイレクトが多すぎます」と表示されることがあります。
対処として、wp-config.php の /* That's all, stop editing! */ の行より上に、次のコードを追加します。WordPress の公式ドキュメントで案内されている方法と同じ考え方です。
// Nginx から HTTPS で転送されてきた場合は HTTPS として扱う
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
このコードはヘッダーの値を信じるので、Apache が 127.0.0.1 でしか待ち受けておらず、Nginx を経由しないと届かない状態であることが前提です。Apache が外部に公開されていると、ヘッダーを偽装したリクエストで HTTPS の判定をごまかされます。
導入後の確認方法
設定が意図どおりかは、次の順で確かめます。
sudo ss -tlnpで、Nginx が0.0.0.0:80と:443、Apache が127.0.0.1:8080で待ち受けていることを確認します。curl -I -H "Host: example.com" http://127.0.0.1:8080/で、サーバー内から Apache が単体で応答することを確認します。- テーマの CSS など静的ファイルの URL に
curl -Iでアクセスし、ExpiresやCache-Controlのヘッダーが付いていることを確認します。付いていれば Nginx が直接返しています。 - Apache のアクセスログに、自分の実際の IP が記録されていることを確認します。
- WordPress の「サイトヘルス」で HTTPS に関する警告が出ていないこと、管理画面へのログインとメディアのアップロードができることを確認します。
よくあるトラブルと原因
リダイレクトがループする: もっとも多い原因は、前述の HTTPS の判定です。wp-config.php の追記と、Nginx の X-Forwarded-Proto の設定を確認します。.htaccess に「HTTPS でなければリダイレクト」というルールを書いている場合も、Apache から見ると常に HTTP なのでループします。その種のリダイレクトは Nginx 側で行うように移します。
リダイレクト先の URL に :8080 が付く: Apache が自分の URL を組み立てるときに内部のポートを使っています。Nginx の proxy_redirect の設定と、Apache の UseCanonicalName が Off になっているかを確認してください。WordPress の「WordPress アドレス」「サイトアドレス」にポート番号が入っていないかも見ておきます。
ログやセキュリティプラグインの IP がすべて 127.0.0.1 になる: mod_remoteip の設定漏れです。セキュリティ系のプラグインには、送信元 IP をどのヘッダーから取るかを選ぶ設定を持つものがあります。項目名はプラグインや版によって異なるため(2026年10月現在)、各プラグインの説明に従って、プロキシ配下である旨を設定してください。
502 Bad Gateway: Apache が停止しているか、待ち受けのポートが Nginx の proxy_pass と一致していません。Nginx の error.log と systemctl status apache2 を確認します。
まとめ
- Nginx 前段・Apache 後段の構成は、
.htaccessを活かしつつ TLS と静的配信を Nginx に任せたいときに選びます。新規構築なら1系統の構成も検討します。 - Apache は
127.0.0.1:8080で待ち受けさせ、mod_remoteip で元の送信元 IP を復元します。 - Nginx では
HostとX-Forwarded-*ヘッダーを渡し、静的ファイルはtry_filesで直接返します。静的ファイルでは.htaccessが効かない点に注意します。 - TLS を Nginx で終端したら、
wp-config.phpで HTTPS の判定を補い、リダイレクトのループを防ぎます。