2026年5月24日
2026年5月24日
ロードバランサーでSSLターミネーションする構成と WordPress 側の設定
SSL ターミネーションとは、HTTPS の暗号化をロードバランサー(LB)で解き、背後の Web サーバーには HTTP(または改めて暗号化した通信)で転送する構成です。証明書の管理を LB に集約でき、Web サーバーの台数を増やしても証明書を配り直す必要がなくなります。一方で、WordPress からは「自分は HTTP で呼ばれている」ように見えるため、何もしないとリダイレクトのループや mixed content が起きます。この記事では、構成の選び方、HAProxy を例にした設定、WordPress 側で必要な対応、確認方法を説明します。
3つの構成と選び方
LB で HTTPS を扱う方法には、主に次の3つがあります。
- ターミネーション(オフロード):LB で復号し、バックエンドへは HTTP で送る。設定が単純で、LB でヘッダーの追加やパスによる振り分けができる
- 再暗号化(ブリッジ):LB で一度復号してから、バックエンドへも HTTPS で送る。LB での振り分けと、内部経路の暗号化を両立できる
- パススルー:LB は TCP のまま中継し、復号はバックエンドが行う。LB は中身を見られないので、HTTP ヘッダーによる振り分けや X-Forwarded-For の付与はできない
LB とバックエンドが同じプライベートネットワーク内にあり、その経路を信頼できるならターミネーションで十分なことが多いです。LB とバックエンドの間がデータセンターをまたぐ、共有ネットワークを通る、業界の基準(クレジットカード情報を扱う場合など)で内部通信の暗号化が求められる、といった場合は再暗号化を選びます。
HAProxy でターミネーションする設定例
HAProxy は、証明書と秘密鍵(必要なら中間証明書)を1つの PEM ファイルにまとめて読み込みます。Let's Encrypt の証明書を使う場合は、更新のたびに結合し直して HAProxy を再読み込みする仕組み(certbot の deploy フックなど)を用意しておきます。
# 証明書・中間証明書・秘密鍵を1ファイルにまとめる(パスは環境に合わせる)
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
| sudo tee /etc/haproxy/certs/example.com.pem > /dev/null
sudo chmod 600 /etc/haproxy/certs/example.com.pem
設定ファイルの例です。HTTP で来たアクセスは HTTPS へリダイレクトし、バックエンドには「元は HTTPS だった」ことを伝えるヘッダーを付けます。
defaults
mode http
option httplog
timeout connect 5s
timeout client 30s
timeout server 60s
frontend fe_web
bind :80
bind :443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
http-request redirect scheme https code 301 unless { ssl_fc }
# クライアントが送ってきた値は捨て、LB が判定した値で上書きする
http-request set-header X-Forwarded-Proto https if { ssl_fc }
option forwardfor
default_backend be_wp
backend be_wp
balance roundrobin
option httpchk
http-check send meth GET uri /healthz.html ver HTTP/1.1 hdr Host www.example.com
http-check expect status 200
server web1 10.0.0.11:80 check
server web2 10.0.0.12:80 check
set-header は同名のヘッダーを置き換えるので、利用者が偽の X-Forwarded-Proto を送ってきても上書きされます。ヘルスチェック用の healthz.html は、WordPress を通らない静的ファイルにしておくと、PHP の不調と Web サーバーの停止を区別しやすくなります。WordPress のトップページをヘルスチェックに使うと、HTTPS へのリダイレクトで 301 が返ってきて「異常」と判定されることがあるので注意してください。
設定を変える前に現在の設定ファイルをバックアップし、haproxy -c -f /etc/haproxy/haproxy.cfg で文法を確認してから再読み込みします。
クラウドの LB(AWS の Application Load Balancer、Google Cloud の外部アプリケーションロードバランサーなど)でも考え方は同じで、HTTPS リスナーに証明書を設定し、バックエンドへ転送します。これらは X-Forwarded-Proto と X-Forwarded-For を自動で付けるのが一般的です。TLS のバージョンや暗号スイートは、事業者が用意したセキュリティポリシーから選ぶ形式が多く、ポリシー名や推奨値は更新されるため(2026年10月現在)、各社のドキュメントで最新のものを確認してください。TLS 1.2 未満を受け付けない設定にしておくのが現在の一般的な水準です。
WordPress 側で必要な設定
HTTPS であることを WordPress に伝える
WordPress は is_ssl() 関数で HTTPS かどうかを判定します。この関数は $_SERVER['HTTPS'] やポート番号を見るため、LB から HTTP で受け取ると「HTTP で呼ばれた」と判断し、管理画面へのアクセスで HTTPS へリダイレクトし続けたり、CSS の URL を http で出力したりします。
wp-config.php の、require_once ABSPATH . 'wp-settings.php'; より上の位置に次を追加します。
// LB が付けた X-Forwarded-Proto を見て HTTPS と扱う
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === strtolower( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
$_SERVER['HTTPS'] = 'on';
}
この設定は、バックエンドが LB からしか到達できないことが前提です。Web サーバーのポートがインターネットに直接開いていると、誰でもこのヘッダーを付けて HTTPS を装えてしまいます。セキュリティグループやファイアウォールで、バックエンドの 80 番ポートへの接続元を LB に限定してください。
あわせて、管理画面の「設定」→「一般」にある WordPress アドレスとサイトアドレスが https:// になっていることも確認します。
利用者の IP アドレスを取り出す
ターミネーション構成では、Web サーバーから見た接続元は LB の IP になります。アクセスログやログイン試行の制限が LB の IP を対象にしてしまうと、1人の攻撃で全員が締め出されることもあります。nginx をバックエンドにしている場合は realip モジュールで、LB の IP 範囲からの X-Forwarded-For だけを信頼させます。
set_real_ip_from 10.0.0.0/24; # LB が属するサブネットに合わせる
real_ip_header X-Forwarded-For;
real_ip_recursive on;
セキュリティプラグインにも「プロキシの背後にある場合の IP の取得方法」を選ぶ設定があることが多いので、LB 構成に合わせて見直します。項目名はプラグインによって異なります。
複数台構成で忘れやすいこと
WordPress のログイン状態は Cookie とデータベースで管理されるため、LB でセッションを特定のサーバーに固定(スティッキーセッション)しなくても動くのが普通です。その代わり、アップロードされた画像が1台目にしか保存されない、プラグインの更新が片方のサーバーにしか反映されない、といった問題が起きます。wp-content/uploads を共有ストレージやオブジェクトストレージに置く、デプロイを全台にそろえる、オブジェクトキャッシュを Redis などで共有する、といった対策を組み合わせます。
動作を確認する
LB の外から HTTPS で接続し、証明書チェーンと、バックエンドから返るヘッダーを確認します。
# 証明書チェーンと TLS のバージョンを確認
openssl s_client -connect www.example.com:443 -servername www.example.com < /dev/null 2>/dev/null \
| grep -E "Protocol|Verify return code|subject=|issuer="
# HTTP から HTTPS へ1回だけリダイレクトされるか
curl -sI http://www.example.com/ | grep -iE "^HTTP|location"
curl -sI https://www.example.com/wp-login.php | grep -iE "^HTTP|location"
Verify return code: 0 (ok) が出ていれば、チェーンに問題はありません。中間証明書が抜けていると、ブラウザでは表示できてもスマートフォンや API クライアントで失敗することがあります。wp-login.php が自分自身へのリダイレクトを繰り返す場合は、wp-config.php の設定が wp-settings.php の読み込みより後ろにないか、LB が X-Forwarded-Proto を付けているかを確認します。
最後に、HSTS(Strict-Transport-Security)を設定するなら LB か Web サーバーのどちらか一方に寄せ、ヘッダーが二重に付かないようにします。最初は短い max-age で試し、問題がないことを確かめてから延ばしてください。
まとめ
- ターミネーション・再暗号化・パススルーの違いは「どこで復号するか」。内部経路を信頼できない場合は再暗号化を選ぶ
- LB は
X-Forwarded-Protoを上書きで付け、ヘルスチェックは WordPress を通らない静的ファイルにする - WordPress には
wp-config.phpで HTTPS を伝える。ただしバックエンドへの接続元を LB に限定することが前提 - 実 IP の取り出し、uploads の共有、全台のデプロイ統一を忘れない
- openssl と curl で、チェーンとリダイレクトの回数を確認する