新しいサブドメインを一つ開くために、DNSレコードを作り、証明書を取得し、サーバーブロックを追加して再起動する。プロジェクトが数百あれば、この作業を数百回やることになる。しかしその大半は、一度だけやっておけば済む作業だ。ワイルドカードDNSとホストルーティングをあらかじめ敷いておけば、新しいサブドメインを開く作業は、データベースに行を一つ追加するだけで終わる。
ここまではきれいな話だ。本題はその先にある。「ワイルドカードDNS」と「ワイルドカード証明書(TLS)」は名前が同じだけで、適用されるルールが違う。この二つを同じルールだと信じ込んだ瞬間、「DNSは確かに応答しているのに、証明書のせいで開かない」という事故が起きる。だからこの記事の中心にある問いは、「何段階まで対応できるのか」ではなく、「いつ対応できないのか」だ。
建物の住所(ドメイン)は一つだが、その中に部屋(サブドメイン)はいくらでも作れる。部屋を一つ増やすたびに外壁を貫通させたりはしない。来訪者は全員が一つの正面玄関から入り、ロビーの受付が「どのお部屋をお探しですか?」と聞いて部屋番号を教えてくれる。この絵を頭に入れておけば、以降の内容はすべてここに収まる。
部屋をもう一つ開く作業は、配管工事ではなく、名簿に名前を一行書き込むことだ。
DNSに *.ontle.kr の形のワイルドカードレコードを登録しておく。ここで言うゾーン(zone)とは、あるドメインのDNSレコードをまとめた単位であり、ラベル(label)とは、ドメインをドットで区切った各断片(report-2026・ontle・kr)のことだ。ゾーンに個別に定義されていない名前への問い合わせが来ると、このワイルドカードが代わりに応答を生成して返す。report-2026.ontle.kr でも dashboard-abc.ontle.kr でも、あらかじめ登録しなくても同じ宛先に向かう。
; ワイルドカード A レコード (直接 IP へ)
*.ontle.kr. 3600 IN A 203.0.113.10
; または CDN/トンネル経由なら CNAME
*.ontle.kr. 3600 IN CNAME edge.example-cdn.net.
よく「ワイルドカードは直下の一段階しかカバーしない」と言われるが、それはDNSのルールではない。どの層のルールなのかは後で改めて整理する。ここでは、土台を一度敷いたというところまで覚えておけばいい。
すべてのリクエストは一つの入口に入ってくる。この入口はリバースプロキシ(入ってくるリクエストを最前面で受けて後ろのサーバーへ渡す中継所)や、CDNエッジ、あるいはアウトバウンドトンネルだ。入口はHTTPリクエストに載った Host ヘッダー — ブラウザがアドレスバーに入れたドメイン名 — を読む。TLSを使う場合はSNI(接続しようとしている名前をあらかじめ伝える値)が同じ役割を果たす。どの扉から入ってきたかは、IPではなく名札で分かれる。
GET / HTTP/1.1
Host: report-2026.ontle.kr
入口の後ろにあるアプリケーションは Host の値を受け取り、「この名前は登録されているか?」を照会する。あれば紐づいたコンテンツを、なければ404や案内ページを返す。名簿は概念的にはこんな姿をしている。
| サブドメイン | コンテンツ種別 | 所有者 | 状態 |
|---|---|---|---|
| report-2026 | 分析ダッシュボード | プロジェクトA | 発行済み |
| jihoon-blog | 個人ブログ | ユーザーB | 発行済み |
| team-x | チームランディング | チームX | 一時停止 |
新しいサブドメインを開く作業が、「DNS追加 → 証明書発行 → サーバー設定 → 再起動」から、この表に一行追加するだけに縮む。DNSと証明書はすでに用意されているので、残るのはデータベーストランザクション一つだけだ。DNSの伝播を待つ理由もない。ワイルドカードレコードはとっくに広まりきった状態で、変わるのはアプリケーション内の名簿だけなので、たいてい数秒以内に反映される。
顧客に納品する分析ダッシュボードを report-2026 という名前で発行したとしよう。report-2026.ontle.kr にアクセスすると、ONTLEのヘッダーやナビゲーションが包むのではなく、そのダッシュボードの元のHTMLがトップレベル文書としてそのまま返される。
違いは体感で表れる。このアドレスをメッセンジャーに送ると、ONTLEのロゴではなく、そのダッシュボードのタイトル・説明・サムネイルが表示される。Google検索にも「ONTLE」ではなく、そのサイトが直接インデックスされる。名刺のように手渡しても、検索エンジンに拾われても、まるごと顧客のものとして見える。これが、ONTLEで成果物を発行したときに 名前.ontle.kr がビューアに閉じ込められず、独立したウェブサイトとして見える理由だ。
ここからが、私が実務で最も多く事故を見てきたポイントだ。二つの「ワイルドカード」は名前が同じだけで、適用範囲のルールが違う。混同した瞬間、「DNSは応答するのに証明書エラーで開かない」という状況に出くわす。
*.ontle.kr レコード): ラベルの数で制限されない。遮る中間ノードさえなければ、a.b.ontle.kr のように何段階も下の名前にも応答を生成する。ただし、すでに存在する名前には適用されない(根拠:RFC 4592)。*.ontle.kr): 最も左のラベル一つだけにマッチする。report-2026.ontle.kr はカバーするが、a.b.ontle.kr や ontle.kr(頂点の名前そのもの)はカバーできない。この「一段階」の制限が、RFC 6125 §6.4.3とCA/Browser Forum Baseline Requirementsが定めたルールであり、後継標準のRFC 9525もそのまま維持している。だから本当の問いは「いつ対応できないのか」だ。名前.ontle.kr のように一段階のものだけを発行する限り、ワイルドカード証明書が一枚あれば十分だ — これがONTLEの基本の発行形態だ。しかし チーム名.顧客社.ontle.kr のように二段階の深さまで降りる名前を開きたいなら、DNSは(中間ノードがなければ)応答しても、TLS証明書は *.ontle.kr 一枚ではカバーできない。その深さを使うには、*.顧客社.ontle.kr の証明書を別途発行するか、証明書のSAN(一枚の証明書がカバーする名前のリスト)にその名前を直接入れる必要がある。「DNSが応答する」と「TLSが信頼される」は別々の層だ。
[発展] ワイルドカードが効かない一箇所 — empty non-terminal
受付のたとえをもう一度持ち出そう。名簿に名前がなくても、その下の個別の部屋にすでに入居者がいれば、受付はそのエリアを「空の名前」とは見なさない。DNSも同じだ。名前が存在する経路は二つある — (1) その名前に直接レコードが付いているか、(2) 自分のレコードはなくても、その下にレコードを持つ下位の名前がある場合だ。二つめがempty non-terminal(空の非終端、ENT)だ。ワイルドカードは「定義されていない名前」だけを受け付けるので、こうして存在する名前には手を出さない。
問題のないケース — DNSは
*.ontle.krのままにし、発行はDBの名簿だけで行う。新しいDNSレコードが生まれないのでENTも生まれない。x.team.ontle.krを発行しても、team.ontle.krはワイルドカード応答をそのまま保つ。落とし穴 —
x.team.ontle.krに実際のDNSレコードを直接打ち込み、上位のteam.ontle.krには何も置かないと、team.ontle.krがENTになる。こうなるとワイルドカードがteam.ontle.krを代わりに受けてくれない。照会するとNXDOMAIN(「名前そのものが存在しない」)ではなくNODATA(「名前はあるが、そのタイプのレコードがない」)が返り、結局開かない。要するに、DBに行を追加するだけならENTは生まれない。 深い名前に個別のDNSレコードを打ち込み始めるときだけ気をつければいい(RFC 4592)。
構造が単純であるほど、運用ルールが緩いと事故が大きくなる。サブドメインを大量に発行するなら、以下は基本として敷いておいたほうがいい。
www、api、admin、mail、blog、app、cdn、status といった名前をユーザーが勝手に取ってしまうと、ユーザーコンテンツがシステム管理ページのように見えたり、逆に実際のシステムサービスが押しのけられたりする。最初から予約リストに入れて防ぎ、入力値は大文字小文字・空白を正規化して比較する。
RESERVED_SUBDOMAINS = frozenset({
"www", "api", "admin", "mail", "ftp", "blog",
"app", "cdn", "status", "docs", "help", "support",
})
def is_reserved(name: str) -> bool:
normalized = name.strip().lower()
return normalized in RESERVED_SUBDOMAINS
名前の割り当てを、アプリケーションロジックの「存在確認してから作成」に任せてはいけない。同じ名前を同時にリクエストした二つのトランザクションが、どちらも「ない」と判断して両方とも挿入してしまうと、一つの名前に二つの成果物が紐づく。正規化した名前のカラムにデータベースのUNIQUE制約をかけ、重複挿入時にDBが出すエラーを、アプリケーション側で「すでに使用中」にマッピングするのが安全だ。
制限なしに発行すると、スパム的な大量生成やリソース枯渇にそのままさらされる。料金プランやアカウント等級ごとに「同時に発行できるサブドメイン数」の上限を設け、超過時は明確に案内して止める。
CDNやエッジキャッシュを使うと、新しく発行した名前がまだキャッシュになくて最初のリクエストが遅かったり、逆に回収した名前がキャッシュに残って古いコンテンツを見せ続けたりする。発行・回収イベントが起きたら、その名前のキャッシュ無効化(purge)を、ルーティングテーブルの更新と一つのトランザクションのように束ねておくのがよい。
ログには成功と記録されているのに、顧客は404を見ている。ルーティングテーブルに行が入ったからといって、そのアドレスが実際に開く保証はない。よくある原因はこうだ — キャッシュが更新されていない、ルーティング条件にタイポがある、(個別のDNSレコードを打ち込んだなら)先に見たENTのせいでDNSの段階ですでに塞がれている、証明書がその深さをカバーできない。
だからサブドメインの発行は、「DBへの記録成功」で終わってはいけない。実際のURLでHTTP 200が返るか、ブラウザで開いて画面が本当に出るかを目で確認するところまでが一セットだ。ログとDBのカウントは「試みた」という証拠であって、「できた」という証拠ではない。
「ルーティングテーブルに
report-2026.ontle.krを追加しました。」 — ここまでは試みだ。そのアドレスにアクセスしたときに画面が出るかは、まだ誰も知らない。「ルーティングテーブルに追加し、
curl -sI https://report-2026.ontle.krでHTTP 200とTLS正常を確認し、ブラウザで開いてダッシュボードがレンダリングされるところまで見ました。」 — ここで初めてできたと言える。
成果物を作るたびにDNS・証明書・デプロイサーバーを再び触る必要はない。ONTLEはこの構造の上で、作った成果物を 名前.ontle.kr という独立したウェブサイトとしてその場で発行する。自分で試してみたいなら https://ontle.kr から始められる。
`*.ontle.kr` 証明書一枚が一段階の名前すべてをカバーするのは、利便性の核心であり代償でもあります。すべてのサブドメインが同じ証明書と秘密鍵に紐づくので、その鍵が漏れると影響範囲が全体に及びます。だからこそ鍵の保管(アクセス権限の最小化)と定期的な交換を運用の基本として押さえる必要があります。特定のサブドメインをより強く保護したいなら、その名前に専用の証明書を追加発行し、そのサーバーでは専用証明書だけを提示するようにできます。ただし専用証明書を発行したからといって、既存の `*.ontle.kr` ワイルドカードがその名前に対して有効性を失うわけではありません — ワイルドカード証明書は依然としてその名前にも有効なので、ワイルドカードの秘密鍵が漏れればその名前を詐称するリスクはそのまま残ります。つまり専用証明書を発行しただけでは、その名前がワイルドカードの適用範囲から外れたり「完全に隔離」されたりはしません(参考:RFC 9525 §7.1)。
DNSが応答するからといってコンテンツがあるわけではありません。登録されていない名前で入ってくると、アプリケーションが名簿にないと判断して404や案内ページに返します。これに予約語リストとアカウントごとの発行数上限を加えれば、「どんな名前でも受け入れる」入口が実際の悪用につながるのを前段で防げます。
DNSはすでに `*.ontle.kr` ワイルドカードが受けているので、新しい名前は名簿(DB)に追加することで吸収されます。ただし過去に個別のDNSレコードを直接打ち込んだ名前があるなら、それを整理する際に、上位の名前がENTにならないかをまず確認する必要があります。その名前自体を引き続きサービングしなければならないなら、明示的なレコードを維持するか、ルーティングを名簿へ移しながら不要な個別レコードを取り除く、という順序で進めてください。
文 · パク・ジフン