ワイルドカードのサブドメイン名をnginxのルーティングに利用する

DNSのサービスで *.example.com のようにワイルドカードサブドメインを設定し、nginxでワイルドカード部分の文字列を見てルーティング先を切り換える、といった構成を行ったので、メモを残します。

ドメイン名の設定

ムームードメインムームDNSを利用していたので、CNAMEの設定でワイルドカード* の文字を使用できた。

*.test としたので、 *.test.example.com のようなワイルドカードの設定となり、 hoge.test.example.com などにマッチする。

nginxのVirtualHost設定

nginxでも server_name ディレクティブで、ワイルドカードの部分は * の文字を使用できる。

Module ngx_http_core_module

まずはすべてのホスト名にマッチする設定をする。

server {
  listen 80;
  root /home/vagrant/www;
  index index.html;
  server_name *.test.example.com;

  location / {
    try_files $uri $uri/ =404;
  }
}

example.com の部分は自分の利用したドメイン名に置き換える。 /home/vagrant/www には適当な動作確認用の index.html ファイルを作成しておく。

この設定で http://hoge.test.example.com/ にブラウザでアクセスすると、 index.html の内容が表示される。

正規表現サブドメインのホスト名部分を抜き出す

ドキュメントの通り、 server_name ディレクティブでは正規表現を使用できるので、 (?<server_host>.+) のようなパターンで一致した文字列を変数名で読めるようしてみる。

Module ngx_http_core_module

また、 if ディレクティブで抜き出したホスト名部分の文字列を使って分岐処理をしてみます。

server {
  listen 80;
  root /home/vagrant/www;
  index index.html;
  server_name ~^(?<server_host>.+).test.example.com$;

  location / {
    if ($server_host != "www") {
      add_header Content-Type text/plain;
      return 200 $server_host;
    }
    try_files $uri $uri/ =404;
  }
}

この設定では、 www.test.example.com 以外の場合は、ホスト名をレスポンスとして返しています。 www.test.example.com の場合は、 try_files ディレクティブにより、 index.html の内容が返されるようになります。

たとえば、 http://hoge.test.example.com/ にブラウザでアクセスした場合は、 hoge と表示されます。

ここまでできると、location以下でルーティングは自由にできるので、たとえばホスト名部分によって proxy_pass の先を変更したりもできます。

サブドメインごとに server ディレクティブを記述してルーティングすることもできますが、運用上この形のほうが嬉しい場合があったので、この形で今回はやってみました。

Let's Encryptでワイルドカード証明書を取得して設定する

ここまでの設定だとHTTPですが、Let's Encrypt(certbot)を使ってワイルドカード証明書を設定すると、HTTPSにすることもできます。

certbotコマンドで以下のようにワイルドカード証明書を取得します。

certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --manual -d *.test.example.com --preferred-challenges dns

この場合、ドメインの所有確認のためにTXTレコードの追加が必要になります。画面に表示された指示にしたがって、TXTレコードをDNSに設定しておきます。

証明書を取得できたらnginxに設定します。HTTPの場合は強制でHTTPSにリダイレクトさせるような場合は以下のようになります。

server {
  listen 80;
  server_name *.test.example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443;
  root /home/vagrant/www;
  index index.html;
  server_name ~^(?<server_host>.+).test.example.com$;

  ssl_certificate /etc/letsencrypt/live/test.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/test.example.com/privkey.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  location / {
    if ($server_host != "www") {
      add_header Content-Type text/plain;
      return 200 $server_host;
    }
    try_files $uri $uri/ =404;
  }
}

2023/12/26追記

ワイルドカード証明書を手動でコマンド実行して取得している場合、自動更新にならない。自動更新されるように追加で設定が必要

参考