2026年10月9日
2026年10月9日
WordPressのNginxで末尾スラッシュとwwwなしURLを統一するリダイレクト設定
同じページが www あり・なし、末尾スラッシュあり・なしの複数の URL で開ける状態は、アクセス解析が分散したり、検索エンジンが正規の URL を判断しにくくなったりする原因になります。WordPress を Nginx で動かしている場合は、「ホスト名と https の統一は Nginx」「末尾スラッシュは WordPress のパーマリンク設定」と役割を分けると、リダイレクトの重複やループを避けながら1回の 301 で正規 URL にそろえられます。この記事ではその設定と確認方法を説明します。
まず役割分担を決める
WordPress には、表示中のページの正規 URL と実際のリクエストが違うときに 301 リダイレクトする仕組み(redirect_canonical())が組み込まれています。パーマリンク設定が /%postname%/ のように末尾スラッシュ付きなら、スラッシュなしでアクセスされた投稿や固定ページは WordPress がスラッシュ付きへ転送します。逆に /%postname% ならスラッシュなしへそろえます。
そのため、末尾スラッシュを Nginx 側で独自に付け外しすると、WordPress のリダイレクトと食い違ってループしたり、REST API(/wp-json/)や管理画面へのリクエストまで書き換えてしまったりする危険があります。おすすめの分担は次のとおりです。
- Nginx:
http→https、www.example.com→example.comをサーバーレベルで1回の 301 にまとめる - WordPress:パーマリンク設定で末尾スラッシュの有無を決め、
redirect_canonical()に任せる - WordPress の設定:「WordPress アドレス(URL)」と「サイトアドレス(URL)」を最終的な URL(例:
https://example.com)にそろえる
最後の項目がずれていると、Nginx が www なしへ転送し、WordPress が www ありへ戻す、という無限リダイレクトになります。作業の前に現在の値を確認しておきます。
wp option get home
wp option get siteurl
手順1:証明書が www とwwwなしの両方を含むか確認する
https://www.example.com/ へのアクセスを転送するには、まず www 付きのホスト名で TLS の接続が成立しなければなりません。証明書に www が含まれていないと、リダイレクトの前にブラウザが証明書エラーを表示します。
sudo certbot certificates
# Domains: の行に example.com と www.example.com の両方があるか確認
足りない場合は、DNS で www が同じサーバーを向いていることを確かめたうえで、両方の名前を含めて証明書を取り直します。
手順2:Nginx の server ブロックを分ける
if ($host = ...) で分岐するより、ホスト名ごとに server ブロックを分けて return 301 を使うほうが、読みやすく処理も軽くなります。設定を変える前に、現在の設定ファイルをバックアップしてください。
# 1) http はすべて https の正規ホストへ(1回で転送)
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
# 2) https の www は www なしへ
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
# 3) 正規ホスト:ここで WordPress を動かす
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/wordpress;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP-FPM への受け渡しなど既存の設定はここに残す
}
ポイントは3つあります。1つ目は、http://www.example.com/ から https://www.example.com/、さらに https://example.com/ と2段階で転送するのではなく、最初の server ブロックで直接最終 URL へ送っていることです。リダイレクトの段数が減るほど表示が速く、検索エンジンにも意図が伝わりやすくなります。
2つ目は $request_uri を使っていることです。これはパスとクエリ文字列を元のまま含むので、?p=123 やキャンペーン用のパラメーターが失われません。$uri を使うとクエリ文字列が落ち、またデコード済みの値になるため、転送には向きません。
3つ目は、Let's Encrypt の HTTP-01 認証との関係です。上の設定では 80 番ポートへのアクセスをすべて https へ転送しますが、Certbot の更新は通常この転送を追いかけて成功します。更新が失敗する環境では、location /.well-known/acme-challenge/ だけを 80 番で直接返す設定を足して certbot renew --dry-run で確認します。
Nginx 1.25.0 以前では http2 on; の代わりに listen 443 ssl http2; と書きます。バージョンは nginx -v で確認してください。
手順3:末尾スラッシュは WordPress の設定でそろえる
管理画面の「設定」→「パーマリンク」で構造を選び、保存します。カスタム構造の末尾に / を付けるかどうかで、投稿・固定ページ・カテゴリーなどの URL の形が決まります。運用中のサイトで変えると全 URL が変わるため、既存サイトでは今の形を維持し、スラッシュなしでのアクセスを WordPress の転送に任せるのが安全です。
なお、Nginx の try_files $uri $uri/ は、実在するディレクトリにスラッシュなしでアクセスされたとき、Nginx 自身がスラッシュ付きへ 301 を返します。リバースプロキシや CDN の後ろでこの転送先が http:// や内部のポート番号になってしまう場合は、absolute_redirect off; を server ブロックに書くと、Location ヘッダーがパスだけの相対形式になり、問題を避けられます。
どうしても Nginx でスラッシュを付けたい場合
静的ページ混在の構成などで Nginx 側での付与が必要な場合は、対象を絞ることが大切です。次は「GET/HEAD のみ」「拡張子なし」「クエリ文字列なし」「wp-json・wp-admin 以外」に限定した例です。
# http ブロック
map "$request_method:$uri:$args" $add_slash {
default 0;
"~^(GET|HEAD):/(?!wp-json/|wp-admin/)[^.?]*[^/]:$" 1;
}
# 正規ホストの server ブロック内
if ($add_slash) {
return 301 https://example.com$uri/;
}
POST リクエストを 301 で転送すると、多くのクライアントは GET に変えて再送するため、フォーム送信や REST API が壊れます。メソッドを絞っているのはそのためです。導入する場合はステージングで、ログイン・コメント投稿・ブロックエディターの保存・問い合わせフォームが動くことを確認してから本番に反映してください。
手順4:既存のコンテンツ内の URL を直す
www ありから www なしへ移行した場合、本文やメニューに古い URL が残っていると、表示のたびにリダイレクトを経由します。WP-CLI で置き換えられますが、データベースを書き換えるので、先にバックアップを取り、--dry-run で件数を確認します。
wp db export before-url-change.sql
wp search-replace 'https://www.example.com' 'https://example.com' --skip-columns=guid --dry-run
# 内容を確認してから --dry-run を外して実行
手順5:反映と確認
sudo nginx -t && sudo systemctl reload nginx
# 各パターンの転送先(Location)を1回ずつ確認
curl -sI http://www.example.com/sample-page/ | grep -iE '^HTTP|^location'
curl -sI https://www.example.com/sample-page/ | grep -iE '^HTTP|^location'
curl -sI https://example.com/sample-page | grep -iE '^HTTP|^location'
# 最終到達までのリダイレクト回数を表示
curl -sIL -o /dev/null -w '%{num_redirects} %{url_effective}\n' http://www.example.com/sample-page
最後のコマンドで、最悪のパターン(http・www あり・スラッシュなし)でも 2 回以内で正規 URL に着けば、ホストの統一とスラッシュの統一がそれぞれ1回ずつ働いています。ブラウザで確認するときは、301 がキャッシュされて古い結果が見えることがあるので、シークレットウィンドウか curl を使うと正しい結果を確認できます。
統一後は、Google Search Console に登録しているプロパティが正規の URL に合っているか、XML サイトマップの URL が正規形になっているかも見直します。どの URL を正規とみなすかは最終的に検索エンジンが判断するため、リダイレクト・canonical タグ・サイトマップ・内部リンクの URL をそろえておくことが大切です。この考え方は Google 検索セントラルのドキュメントに基づいています(2026年10月現在)。
まとめ
- ホスト名と https の統一は Nginx の server ブロック分けと
return 301で、1回の転送にまとめる - 転送先には
$request_uriを使い、パスとクエリ文字列を保つ - 末尾スラッシュはパーマリンク設定と WordPress の
redirect_canonical()に任せるのが基本 - Nginx で付与するなら GET/HEAD・拡張子なし・wp-json と wp-admin 除外に絞る
- WordPress の home・siteurl と証明書の対象ホストを最終 URL に合わせ、curl で転送回数を確認する