2026年6月6日
2026年6月6日
Nginx でロードバランサーを構築する手順|専用サーバーの用意からHTTPS終端・WordPress側の設定・動作確認まで
Nginx は、Web サーバーとしてだけでなく、複数台のサーバーに通信を振り分けるロードバランサー(LB)としても使えます。この記事では、Nginx を入れた専用の LB サーバーを1台用意し、その後ろに WordPress サーバー2台を置く構成を、構築の順番どおりに説明します。読み終えると、LB の設定、HTTPS の終端、WordPress 側で必要な調整、振り分けの確認方法までを一通り自分で組めるようになります。分散方式(ラウンドロビンや least_conn など)の細かな比較は扱わず、「動く LB を正しい順番で作る」ことに絞っています。
完成形の構成と作業の順番
想定する構成は次のとおりです。
- LB サーバー:グローバル IP を持ち、80/443 番で外部からの通信を受ける。プライベート IP は 10.0.0.10
- WordPress サーバー:10.0.0.11 と 10.0.0.12。外部からは直接接続させず、LB からの通信だけを受ける
- データベース:WordPress サーバーから共通で使う(別サーバーやマネージド DB)
作業は「LB を HTTP で動かす → バックエンドで送信元を正しく扱う → HTTPS を終端する → 振り分けと障害時の切り離しを確認する」の順で進めます。最初から HTTPS や細かな調整を入れると、問題が起きたときにどの段階が原因か分からなくなるためです。本番に入れる前に、同じ構成をステージング環境で組んで試してください。
ステップ1:LB サーバーに Nginx を入れる
Ubuntu / Debian 系なら、ディストリビューションのパッケージで入れるのが手軽です。より新しい版が必要な場合は、nginx.org が提供する公式リポジトリも使えます。
sudo apt update
sudo apt install nginx
nginx -v
バージョンによって使えるディレクティブが異なるため、nginx -v の結果は控えておきます。たとえば HTTP/2 の有効化は、新しい版では listen の http2 パラメータではなく http2 on; という独立したディレクティブで書く方式に変わっています(2026年10月現在)。
インストール直後に有効になっている既定のサイト設定は、LB 用の設定と競合しやすいので無効にしておきます。
sudo rm /etc/nginx/sites-enabled/default
これはシンボリックリンクを外すだけで、元の設定ファイルは /etc/nginx/sites-available/ に残ります。
ステップ2:upstream と転送の設定を書く
/etc/nginx/conf.d/lb.conf を作り、振り分け先と転送のしかたを書きます。
upstream wordpress_backend {
server 10.0.0.11:80 max_fails=3 fail_timeout=30s;
server 10.0.0.12:80 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://wordpress_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_next_upstream error timeout http_502 http_503;
}
}
それぞれの行には理由があります。
Hostを引き継がないと、バックエンドの WordPress が別のドメインへのアクセスと解釈し、リダイレクトが繰り返されることがあるkeepaliveとproxy_http_version 1.1、空のConnectionヘッダーの3つをそろえると、LB とバックエンドの接続が使い回され、接続確立の負荷が減るmax_failsとfail_timeoutは、失敗が続いたサーバーを一定時間振り分け対象から外す設定。オープンソース版の Nginx は、実際のリクエストの失敗を見て判断する「受動的な」チェックのみで、定期的に確認しにいく能動的なヘルスチェックは商用版(NGINX Plus)の機能proxy_next_upstreamは、接続エラーやタイムアウト時に別のサーバーで再試行する条件。POST のような冪等でないリクエストは、既定では再送されない
書いたら文法を確認してから反映します。
sudo nginx -t
sudo systemctl reload nginx
設定に誤りがある状態で restart すると Nginx が起動できず、LB 自体が止まります(reload なら古い設定のまま動き続けます)。この2行を毎回セットで実行する習慣にすると事故が減ります。
ステップ3:バックエンド側でクライアントの IP を正しく扱う
LB を通すと、WordPress サーバーから見た接続元はすべて LB の IP(10.0.0.10)になります。このままだと、アクセスログ、コメントの投稿者 IP、ログイン試行の制限などが LB の IP で記録されてしまいます。バックエンドの Nginx に realip モジュールの設定を入れ、LB から届いたときだけ X-Forwarded-For の値を接続元として扱わせます。
# バックエンド(10.0.0.11 / 10.0.0.12)の server ブロック内
set_real_ip_from 10.0.0.10;
real_ip_header X-Forwarded-For;
set_real_ip_from に LB 以外のアドレスを広く書くと、クライアントが偽の X-Forwarded-For を付けるだけで IP を詐称できてしまいます。信頼する範囲は LB のアドレスに限定してください。あわせて、バックエンドの 80 番はファイアウォールで LB からの接続だけを許可します。
# ufw の例(バックエンド側)
sudo ufw allow from 10.0.0.10 to any port 80 proto tcp
ステップ4:LB で HTTPS を終端する
HTTP で振り分けが動くことを確認できたら、LB に証明書を入れて HTTPS を終端します。Let's Encrypt を使う場合は、Certbot の Nginx プラグインで取得と設定の追記ができます(パッケージ名や推奨の入れ方は Certbot の公式手順を確認してください)。
sudo certbot --nginx -d example.com
LB とバックエンドの間はプライベートネットワーク内の HTTP のままにする構成が一般的です。この場合、WordPress からは HTTP で接続されたように見えるため、ステップ2で付けた X-Forwarded-Proto を見て HTTPS と判定させる設定を wp-config.php に入れます。
// LB 経由で HTTPS だったときだけ HTTPS として扱う
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
これを入れないと、管理画面でリダイレクトが繰り返される、画像や CSS が http:// で読み込まれて混在コンテンツの警告が出る、といった症状が起きます。ステップ3のファイアウォール設定で、バックエンドに外部から直接届かないようにしておくことが、このヘッダーを信頼する前提です。
ステップ5:WordPress を複数台で動かすための準備
LB の設定が正しくても、WordPress 側が1台構成のままだと不具合が出ます。
- アップロードファイル:管理画面から画像を上げると、受け付けた1台にしか保存されない。共有ストレージやオブジェクトストレージへのオフロードで全台から同じファイルを見られるようにする
- プラグインやテーマの更新:1台だけ更新されると、台ごとにコードが食い違う。管理画面からの更新を止め、デプロイの仕組みで全台に配る
- セッション:WordPress のログイン状態は Cookie とデータベースで管理されるため、DB を共有していれば台をまたいでも維持される。PHP セッションを使うプラグインがある場合は、共有のセッション保存先を用意するか、
ip_hashなどで同じ台に振り分ける - オブジェクトキャッシュ:Redis などを使う場合は全台で同じサーバーを参照する
- WP-Cron:各台で動くと重複実行の原因になるため、
DISABLE_WP_CRONを設定し、1か所からシステムの cron で実行する
ステップ6:振り分けと切り離しを確認する
どのサーバーに振り分けられたかをログで見られるよう、LB の http コンテキストにログ形式を追加します。
log_format upstream_log '$remote_addr [$time_local] "$request" $status '
'upstream=$upstream_addr up_status=$upstream_status '
'up_time=$upstream_response_time';
access_log /var/log/nginx/lb_access.log upstream_log;
外部から何度かアクセスし、upstream= の値が2台に分かれていれば振り分けは動いています。
for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/; done
sudo tail -n 10 /var/log/nginx/lb_access.log
次に、片方のバックエンドの Nginx を止めてアクセスを続け、エラーにならず残りの1台に寄ることを確かめます。受動的なチェックのため、止めた直後の数リクエストで失敗が検知され、その後 fail_timeout の間は外されます。復旧後に再び振り分けが戻ることも確認しておきます。
LB 自体を単一障害点にしない
ここまでの構成では、LB が止まるとサイト全体が止まります。本番運用では、LB を2台にして Keepalived(VRRP)で仮想 IP を引き継ぐ構成や、クラウドのマネージドロードバランサーを前段に置く構成を検討してください。クラウドでは仮想 IP の扱いに制約があることが多いため、利用するサービスの推奨構成を確認します。設定変更の前には、/etc/nginx/ 一式をバックアップしておくと、問題が出たときにすぐ戻せます。
まとめ
- LB は「HTTP で振り分け → 送信元 IP の処理 → HTTPS 終端 → 確認」の順に組むと、問題の切り分けがしやすい
HostとX-Forwarded-*ヘッダーを引き継ぎ、keepalive の3点セットでバックエンドとの接続を使い回す- オープンソース版 Nginx のヘルスチェックは受動的で、
max_failsとfail_timeoutで切り離す - バックエンドは LB からの接続だけを許可し、その前提で realip と HTTPS 判定を設定する
- アップロード・コードの配布・WP-Cron など、WordPress 側の複数台対応も同時に進める
- LB の冗長化まで考えて、初めて単一障害点のない構成になる