2026年6月5日
2026年6月5日
マルチテナント用サブドメインのリダイレクト手順|HTTPS統一・テナント名変更・存在しないテナントの扱い
acme.example.com のように、テナント(顧客や店舗)ごとにサブドメインを割り当てるサービスでは、リダイレクトの設計を最初に決めておくと後の変更が楽になります。対象になるのは、HTTPからHTTPSへの統一、テナント名を変えたときの旧名からの転送、パス型のURLからの移行、存在しないテナントへのアクセスの扱いの4つです。この記事では、リダイレクト表の作り方から、Nginxでの設定例、WordPressマルチサイトでの注意点、転送先を安全に検証する方法、確認の手順までを説明します。
先にリダイレクト表を作る
設定ファイルを書き始める前に、「どこからどこへ、どのステータスコードで、なぜ転送するか」を表にまとめます。テナント向けのサブドメインでは、次のパターンがよく出てきます。
| 転送元 | 転送先 | コード |
|---|---|---|
http://acme.example.com/... |
https://acme.example.com/... |
301 |
https://old-name.example.com/...(名前変更前) |
https://new-name.example.com/... |
301 |
https://example.com/t/acme/...(旧パス型) |
https://acme.example.com/... |
301 |
https://unknown.example.com/(未登録) |
転送しない。404と案内ページ | 404 |
ポイントは2つです。1つ目は、パスとクエリ文字列を引き継ぐことです。テナントのトップページにまとめて飛ばすと、ブックマークや共有されたリンクがすべて役に立たなくなります。2つ目は、存在しないテナントをメインサイトのトップへ301で飛ばさないことです。打ち間違えた利用者が理由も分からずトップに戻されるうえ、検索エンジンからは中身の無いページ(ソフト404)と見なされることがあります。
また、恒久的な変更には301、キャンペーンなど後で戻す可能性がある転送には302を使います。301はブラウザに長く記憶されるので、テスト中は302で動作を確かめ、問題が無ければ301に切り替えると安全です。
Nginxでの設定例
ここでは、すべてのテナントを1つのアプリで処理し、Nginxをその前に置く構成を例にします。ワイルドカード証明書(*.example.com)とワイルドカードDNSは用意済みとします。作業前に設定ファイルのバックアップを取り、検証環境で試してから本番に反映してください。
# http ブロック内: 名前を変えたテナントの対応表(旧名 新名)
map $tenant $tenant_new {
default "";
old-name new-name;
shop-test shop;
}
# HTTP → HTTPS(名前変更もここで済ませ、転送を1回にする)
server {
listen 80;
server_name ~^(?<tenant>[a-z0-9-]+)\.example\.com$;
set $target $tenant;
if ($tenant_new) {
set $target $tenant_new;
}
return 301 https://${target}.example.com$request_uri;
}
# テナントのサブドメイン(HTTPS)
server {
listen 443 ssl;
server_name ~^(?<tenant>[a-z0-9-]+)\.example\.com$;
# ssl_certificate / ssl_certificate_key はワイルドカード証明書を指定
if ($tenant_new) {
return 301 https://${tenant_new}.example.com$request_uri;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://127.0.0.1:8080;
}
}
server_name の正規表現で、サブドメインの部分を $tenant という変数に取り出しています。名前変更の対応表を map にしておくと、テナント名を変えるたびに1行足すだけで済みます。変数名の後ろに . が続くので、${target} のように波かっこで囲んで区切りをはっきりさせています。
www.example.com や example.com も、このままだと正規表現に一致してテナントとして扱われてしまいます。Nginxでは、名前を正確に書いた server_name が正規表現より優先されるので、メインサイト用の server ブロックに server_name example.com www.example.com; を明記しておきます。www、api、admin などはテナント名として登録できないよう、アプリ側で予約語にしておくと衝突を防げます。
パス型のURLから移行する場合
以前 example.com/t/acme/ のようなURLでテナントを分けていたなら、メインサイト側の server ブロックに次のような転送を入れます。
location ~ ^/t/(?<legacy_tenant>[a-z0-9-]+)(?<legacy_path>/.*)?$ {
return 301 https://${legacy_tenant}.example.com${legacy_path}$is_args$args;
}
正規表現で取り出したパスは、Nginxが整えた後の値(URLエンコードを戻した形)です。日本語や記号を含むURLがある場合は、転送後のURLが正しいかを個別に確認してください。
存在しないテナントはアプリ側で判断する
どのテナントが登録済みかはNginxには分からないので、アプリ側で判断し、未登録なら404を返して「このアドレスのサイトは見つかりません」といった案内を表示します。解約したテナントは、しばらく「サービスを終了しました」と表示するなど、運営方針に合わせて決めます。
WordPressマルチサイトの場合
WordPressマルチサイトをサブドメイン型で使っている場合、存在しないサイト名にアクセスすると、WordPressは登録ページやメインサイトへ転送します。転送先を変えたいときは、wp-config.php に NOBLOGREDIRECT を定義します。
define( 'NOBLOGREDIRECT', 'https://example.com/site-not-found/' );
この定数はメインサイトの404の扱いにも影響する場合があるので、検証環境で、存在しないサイト名とメインサイトの存在しないページの両方を開いて動作を確かめてから本番に入れてください。
サイトのURL(テナント名)を変えたときは、旧サブドメインのWordPressのサイトはもう存在しないため、旧名からの転送はWordPressの中ではなく、上のNginxの map のようにWebサーバー側で行うのが分かりやすい方法です。
転送先は許可した名前だけに限る
テナントのアプリやWordPressで、ログイン後などに「元のページへ戻す」転送を作るときは、オープンリダイレクトに注意します。URLパラメータで受け取った転送先や、リクエストのHostヘッダーをそのまま転送先に使うと、第三者のサイトへ誘導する踏み台にされる恐れがあります。
WordPressでは、wp_redirect() ではなく wp_safe_redirect() を使うと、許可されたホスト以外への転送が拒否されます。マルチサイトのサブドメインは別のホストとして扱われるので、実在するサイトだけを許可するフィルターを、mu-plugin(wp-content/mu-plugins/ に置くプラグイン)として追加します。
<?php
/**
* Plugin Name: Tenant Redirect Hosts
* Description: wp_safe_redirect() で、実在するテナントのサブドメインへの転送だけを許可する
*/
add_filter( 'allowed_redirect_hosts', function ( $hosts, $host ) {
$host = strtolower( (string) $host );
if ( is_multisite()
&& preg_match( '/^[a-z0-9-]+\.example\.com$/', $host )
&& get_blog_id_from_url( $host, '/' ) ) {
$hosts[] = $host;
}
return $hosts;
}, 10, 2 );
wp_safe_redirect() は、許可されていない転送先を渡されると管理画面のURLへ転送先を差し替えます。呼び出した後は exit; で処理を止めてください。example.com の部分は自分のドメインに書き換えます。
HTTPSとHSTSの注意点
テナントが増えるサービスでは、ワイルドカード証明書でサブドメインをまとめてHTTPS化するのが一般的です。ワイルドカード証明書は1階層だけが対象なので、a.acme.example.com のような2階層のURLを作らない設計にしておきます。
HSTSに includeSubDomains を付けると、すべてのテナントがHTTPSでしか開けなくなります。HTTPのまま残っているサブドメイン(古い検証環境など)が無いことを確かめてから、最初は短い max-age で試し、問題が無ければ延ばしてください。
設定後の確認
リダイレクト表の各行を、curl でまとめて確かめます。
for u in \
http://old-name.example.com/news/?page=2 \
https://old-name.example.com/news/ \
https://example.com/t/acme/about/ \
https://unknown-xyz.example.com/ ; do
curl -s -o /dev/null -L -w "%{http_code} 転送%{num_redirects}回 %{url_effective} <- ${u}\n" "${u}"
done
見るべき点は3つです。最終的なURLが表のとおりか、パスとクエリが引き継がれているか、転送の回数が1回で済んでいるかです。HTTPの旧名からHTTPSの旧名、さらにHTTPSの新名へと2回以上転送されているなら、上の例のように最初の転送でまとめると、表示が速くなり検索エンジンにも変更が伝わりやすくなります。Googleは、恒久的なリダイレクトをできるだけ長く(目安として1年以上)残すよう案内しています(2026年10月現在)。
まとめ
- 先にリダイレクト表を作り、HTTPS統一・テナント名変更・パス型からの移行・未登録テナントの扱いを決めます。パスとクエリは引き継ぎます。
- 存在しないテナントはトップへ301で飛ばさず、404と案内ページで応えます。
- Nginxでは正規表現の
server_nameでテナント名を取り出し、名前変更はmapで管理します。メインサイトは正確な名前で別に定義します。 - アプリ内の転送は許可したホストだけに限り、WordPressでは
wp_safe_redirect()とallowed_redirect_hostsフィルターを使います。