よくある例を挙げる。marketing.example.com のCNAMEをWebflow向けから新しいセルフホストのランディングページ向けに切り替え、DNS管理画面で変更が反映されたのを確認する。5分後、同僚から「私のラップトップだとまだ古いデザインのままなんだけど」と連絡が入る。dig +short marketing.example.com を叩くと新しいIPが返ってくる。同僚のラップトップは別のリゾルバに問い合わせていて、そのリゾルバのキャッシュにはまだ古いレコードが残っているのかもしれない。どちらのキャッシュが間違っているのか?
正直に言えば「どちらも間違っていない、変更がまだすべての再帰リゾルバに届いていないだけだ」となる——しかしこれを、ひとつのリゾルバに対する一発の dig だけでは証明できない。複数のパブリック再帰リゾルバに、速やかに問い合わせて結果を比較する必要がある。これまでこれを実現する手段は、CLIのループ、有料のネットワーク監視ツール、あるいは複数地域から照会する Web サイトのいずれかだった。ブラウザは役に立たなかった——Webページは UDPポート53のソケットを開けないので、古典的なDNSを話せない。それを変えたのが、2018年にRFC 8484がDNS-over-HTTPSを標準化し、CloudflareとGoogleがクロスオリジンのリクエストを受け付けるパブリックDoHエンドポイントを公開したことだった。いまは fetch() で HTTPS リクエストを1回送るだけで、どのレコードタイプでも問い合わせられる。JSON エンドポイントの応答はただのJSONだ。
ZeroToolのDNS Lookupはそのエンドポイントを包んだものだ。中身は自分で書くであろう fetch() そのもので、それにタイプごとにまとめたレコードカード、dig 風の生表示、そして ALL を選んだときに最頻出の8タイプを並列照会する機能が付いている。以下は運用ガイドだ——DoHが実際にどう動くのか、各レコードタイプが実務的に何を語ってくれるのか、そしてDNSのデバッグで見当違いの足場に立ったときに必ず噛みつかれる5つの落とし穴。
DNS-over-HTTPSを一段落で
伝統的なDNSクエリは UDP/53(切り詰めが起きた場合は TCP/53)の上に乗ったバイナリパケットだ。ブラウザはそのどちらに対してもソケットAPIを公開していない。DoHは、同じクエリを HTTPS リクエストとして符号化することでこれを解決する。エンコーディングは2種類ある——RFC 8484 のバイナリ方式(application/dns-message ボディをもつ POST、または GET ?dns= の中に base64url で詰めた形式)と、Googleが先行して採用しCloudflareも追随したJSONプロファイル(GET ?name=...&type=... でJSONを返す)。JSONプロファイルはどのRFCにも載っていない。Cloudflare のドキュメントは Google のスキーマに従っていると書いており、フィールドは共通だ。細かな違いは後述する:
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": false,
"CD": false,
"Question": [{ "name": "zerotool.dev", "type": 1 }],
"Answer": [
{ "name": "zerotool.dev", "type": 1, "TTL": 300, "data": "172.67.170.64" },
{ "name": "zerotool.dev", "type": 1, "TTL": 300, "data": "104.21.95.91" }
]
}
Status は標準的なDNSのRCODE(RFC 1035 §4.1.1、RFC 6895で拡張)。AD はDNSSECの「Authenticated Data」フラグで、両社のドキュメントとも、応答中のすべてのレコードが検証されたときだけ true になると説明している。RFC 6840 §5.8 は、問い合わせに DO か AD ビットがあるときだけ AD を立てるようリゾルバに求めている。do=1 なしの場合、Cloudflare の AD は一定しない(後述)。TC は切り詰めフラグで、Cloudflare のドキュメントによれば、最大サイズの応答に対応しているため DoH ではほぼ常に false になる。
ツールは do=1(DNSSEC OK)と cd=0(Checking Disabled = 0)を送る。cd=0 はリゾルバに DNSSEC の検証を求め、do=1 は署名を応答に含めるよう求める。do=1 なしだと Cloudflare の AD は当てにならない。2026-09-30 に各クエリを 30 回ずつ送ると、example.com の A・AAAA・NS は毎回 "AD": false、MX は毎回 true だった。それより前に A を 20 回送ったときは true と false が 10 回ずつだった。do=1 付きの照会と Google への照会はすべて true だった。AD で判断するなら do=1 を付ける必要がある。逆の cd=1 は「検証はスキップしてくれ」という指示で、壊れたゾーンをフォレンジック的に観察するときには有用だが、通常のルックアップで欲しい挙動ではない。
10種のレコードタイプと、それが教えてくれること
ツールが公開しているのは10タイプ。それぞれが特定の運用上の問いに答える。間違ったタイプを選ぶと、ありもしない設定ミスを何時間も追いかけることになる。
| タイプ | コード | 問い | 欠落・誤設定時に出やすい運用障害 |
|---|---|---|---|
A | 1 | 「この名前を提供するIPv4アドレスは?」 | 開発者のマシンでは開けるサイトが、他人のマシンでは404——古いAと新しいAが食い違っている。 |
AAAA | 28 | 「この名前を提供するIPv6アドレスは?」 | AAAAが古いホストを指したままだと、IPv6を優先するクライアントからはサイトが開けない。IPv4だけの環境で試す人には問題が見えない。 |
CNAME | 5 | 「このエイリアスはどの正規名を指している?」 | 削除済みのHerokuアプリを指したままのCNAME、SaaSの別テナントを指したままのCNAME。 |
MX | 15 | 「このドメイン宛のメールを受けるサーバーは?」 | すべての受信メールが「no MX record found」で跳ね返る、あるいは移行後に間違ったプロバイダへルーティングされる。 |
TXT | 16 | 「どんなポリシー文字列が公開されている?」 | SPFがソフトフェイル、DMARCレポートで正規のメールがスパム判定、ドメイン所有確認が「pending」のまま止まる。 |
NS | 2 | 「このゾーンの権威サーバーは誰?」 | レジストラ移管後にNSが旧プロバイダを指したまま——更新がそもそも伝播しない。 |
SOA | 6 | 「プライマリサーバーは誰で、セカンダリはどの頻度でリフレッシュすべき?」 | セカンダリがSOAのリフレッシュウィンドウを過ぎてキャッシュしていたために、権威ゾーンが古いデータを返し続ける。 |
CAA | 257 | 「このドメインに対して証明書発行を許可されたCAは?」 | 記載のないCAは発行を拒否するので、CAやCDNを移したあとに更新が失敗する。 |
PTR | 12 | 「このIPは自分が何の名前だと主張する?」 | 送信元IPのPTRがHELOドメインと一致しないせいでメールが跳ね返る。レピュテーションサービスに送信者として晒される。 |
SRV | 33 | 「サービス _xmpp-client._tcp(など)はどこにある?」 | SRVが欠けていたりポートが違っていたりして、XMPP、SIP、LDAP、Matrixフェデレーションがホストを見つけられない。 |
ツールの ALL モードは、Promise.all で先頭8タイプへの並列リクエストを同時にファンアウトする。これは最頻出の運用上の問い「このゾーンについてわかることを全部見せて」に対する正しいデフォルトだ。PTRとSRVがALLから外れているのは、PTRがドメイン名ではなくin-addr.arpa形式(例:34.216.184.93.in-addr.arpa)の入力を要求するためで、SRVはアンダースコア接頭辞付きの特定の名前(_xmpp-client._tcp.example.com)を要求するためだ。両者は一括スイープの一部としてではなく、単発のターゲットクエリとして使うのが筋にかなっている。
なぜ自分の dig とツールが食い違うのか
最頻出の意外な現象——開発者のラップトップで叩いた dig と、ブラウザからのDoHが、同じ名前に対して異なる答えを返す。退屈な理由が4つと、興味深い理由が1つある。
ローカルの再帰キャッシュ。 ラップトップ、ルーター、社内フォワーダのいずれもが、各レコードのTTLぶんDNSをキャッシュする。CNAMEに 86400(24時間)のTTLが付いていれば、権威サーバー上での変更がラップトップに届くまで最大24時間かかる。DoH呼び出しはCloudflareかGoogleに問い合わせる——彼らのキャッシュはあなたのキャッシュとは独立している。Cloudflare と Google でそれぞれ1回ずつ照会して答えを比べてみよう。Cloudflareが新しい値を返し、Googleが古い値を返すなら、伝播の途中を目撃している。
スプリットホライズンDNS。 企業ネットワーク、VPN、Active Directory環境では、社内向けの権威リゾルバが同じ名前に対して別の答えを返すのが日常茶飯事だ。internal-tools.example.com がファイアウォール内では 10.42.0.5 に解決し、外からは NXDOMAIN になる、という具合。DoH呼び出しは外側の見え方を見ている。会社のマシンでは動く名前にツールがNXDOMAINを返すなら、そのゾーンはスプリットホライズンで、その名前のことをパブリックDNSは知らない。
ジオルーティングされた応答。 Cloudflare、Akamai、AWS Global Accelerator、ほとんどのCDNは、クエリを受け取ったエッジPOPに応じて異なる A/AAAA レコードを返す。ツールに表示されるIPは、CloudflareやGoogleの再帰リゾルバが彼らのネットワーク位置から見たものであって、あなたから見たものではない。「このIPは正しい?」という問いについては、ツールは一般のパブリックビューワにとっての正解だ。「私のPOPから見てこのIPは正しい?」を問うなら、そのPOPから問い合わせる必要がある。
EDNS Client Subnet(RFC 7871)。 一部の権威サーバーは、再帰リゾルバがクライアントのサブネットのヒントを転送してくれた場合に、より正確なジオルーティング応答を返す。Google Public DNS のドキュメントには「通常はおおよそのネットワーク情報を送る(多くの場合 IPv4 アドレスの最後の部分をゼロにする)」とあり、Cloudflare の FAQ は 1.1.1.1 が ECS を送らないと書いている。そのため同じジオルーティングされた名前でも、両者で異なるIPが返ることがあり、Google の答えは照会元の位置で変わる。Google の JSON API では edns_client_subnet パラメーターでサブネットを指定できる(ツールはこのパラメーターを送らない)。2026-09-30 に www.qq.com を照会すると、114.114.114.0/24 では 121.14.77.201 と 121.14.77.221、8.8.8.0/24 では 43.159.109.55、139.130.4.0/24 では 43.168.224.173 が返った。Cloudflare は同じパラメーターを無視し、43.159.109.55 を返した。
興味深いやつ——古い負キャッシュ。 RFC 2308 は NXDOMAIN 応答もキャッシュ対象だと定めている。存在しないレコードには自分のTTLがないので、負キャッシュの時間は SOA レコード自身のTTLと MINIMUM フィールドの小さいほうになる(RFC 2308 §5)。あるゾーンが1時間だけ設定ミスで、その間にローカルの再帰リゾルバがNXDOMAINをキャッシュしてしまうと、ツールは正しい答えを返すのに、ローカルからのクエリはその時間が過ぎるまで失敗し続ける。対処法は——待つ。あるいはゾーン変更前に SOA の MINIMUM を下げておく。
サマリーピルの読み方
レコードビューの上に5つのピルが並ぶ。それぞれが運用上のシグナルを1つに圧縮している:
- NOERROR / NXDOMAIN / SERVFAIL —— RCODE。NOERROR はクエリが正しく処理されたという意味で、付随するレコードがゼロのときは「この名前は存在するが、このタイプのレコードは持っていない」を意味する。NXDOMAIN はその名前がゾーンのどこにも存在しないことを示す。SERVFAIL は再帰リゾルバがクエリを完了できなかったという意味で、DNSSEC検証の失敗か、権威サーバーからのエラー応答が原因であることが多い。
- N records —— Answer セクションのレコード数。NOERROR で 0 件が返ってきた場合、その名前に対しては選んだタイプが間違った問いだということだ。実際に欲しいゾーンのNSを確認しよう。
- DNSSEC ✓ / no DNSSEC / DNSSEC failed —— AD フラグ。「no DNSSEC」のピルは、ゾーンが署名されていないか、応答中の CNAME が署名のないゾーンにつながっていることを意味する。署名の検証に失敗した場合は SERVFAIL が返る。SERVFAIL に DNSSEC 関連の拡張エラーコード(RFC 8914 の 1・2・5〜12)が付いていればピルは「DNSSEC failed」になり、それ以外の SERVFAIL ではピルを出さない。リゾルバが判断材料になる回答を返していないからだ。
com・org・devの TLD は署名済み(ルートゾーンにそれぞれの DS レコードがある)だが、その下のドメインは所有者が DNSSEC を有効にしたときだけ署名される。example.comは署名済み、zerotool.devは未署名だ。 - Resolver —— どのDoHエンドポイントが応答したか。CloudflareとGoogleを横並びで比べるときに役立つ。
- Latency ——
fetch()の開始から JSON パースまでのラウンドトリップ。最初の照会にはリゾルバとの HTTPS 接続の確立が含まれるので、2回目以降より遅くなる。
同じクエリを自分で書くなら
ツールが存在する理由は、単発のルックアップに対してはクリックがスクリプトを上回るからだ。自動化のためであれば、DoHのJSONプロファイルは fetch を持つ任意の言語にそのまま落とし込めるほど短い。文脈に応じて使い分けられるリファレンス実装を3つ挙げる。
ブラウザ/Node 18+ 版は、ドメインさえあれば2行:
const url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent('zerotool.dev')}&type=MX&do=1&cd=0`;
const res = await fetch(url, { headers: { accept: 'application/dns-json' } });
const json = await res.json();
console.log(json.Answer); // [{ name, type, TTL, data }, ...]
タイムアウトが欲しければ AbortSignal.timeout(5000) を、ネットワークが落ちる可能性があるなら try/catch を足す。CloudflareとGoogleのDoHはどちらも上記と一致するJSON構造を返すので、URLを差し替えるだけでプロバイダを切り替えられる。
Python 版は httpx か urllib を使う。標準ライブラリのみでも書ける:
import json
import urllib.request
def doh(name: str, qtype: str, resolver: str = "cloudflare"):
if resolver == "google":
url = f"https://dns.google/resolve?name={name}&type={qtype}&cd=0"
headers = {}
else:
url = f"https://cloudflare-dns.com/dns-query?name={name}&type={qtype}&cd=0"
headers = {"accept": "application/dns-json"}
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=5) as resp:
return json.loads(resp.read())
print(doh("zerotool.dev", "CAA"))
シェルスクリプトや CI ステップ向けには、curl と jq の組み合わせが定番だ。以下のワンライナーは、ドメインのすべてのMXターゲットをプライオリティ順に取得する:
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=cloudflare.com&type=MX' \
| jq -r '.Answer[] | "\(.data)"' | sort
これを for ループで包めばターミナルを離れずに自前の伝播トラッカーが手に入る。Bash関数にして ~/.bashrc に入れれば、アドホック用途で dig の半分を置き換えたことになる。
TXTセグメンテーション —— 255バイトのルール
長いSPF、DKIM、DMARCレコードは、RFC 1035 §3.3.14 が TXT レコード内の個々の文字列に課している255バイト上限を日常的に超えてしまう。DNSプロトコルは1つのTXTレコードに複数の文字列を持たせることを許しており、SPFでの慣例(RFC 7208 §3.3)は、論理的な1レコードを構成するためにそれらを区切り文字なしで連結するというものだ。2つの JSON API は文字列の見せ方が違う。Cloudflare は dig と同じく文字列ごとに引用符で囲んで空白で区切り、Google は引用符なしの1つの値につなげて返す。Cloudflare の形式:
{
"name": "google.com",
"type": 16,
"TTL": 300,
"data": "\"v=spf1 include:_spf.google.com ~all\""
}
より長いレコードでは隣接する2つの文字列として返る:
{
"data": "\"v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; \" \"fo=1; aspf=r; adkim=r\""
}
ツールはリゾルバから届いた値をそのまま表示する。Cloudflare の形式から論理値を得るには、外側のクォートと、セグメント間のクォート・スペース・クォート(" ")を取り除く。SPF(RFC 7208 §3.3)も DKIM(RFC 6376 §3.6.2.2)も、文字列を何も挟まずにつなげると定めている。Google の形式はすでにつながっている。cf2024-1._domainkey.zerotool.dev の DKIM 公開鍵では、Cloudflare は 255 文字の最初の文字列のあとに " " を1つ挟んだ 425 文字を返し、Google は同じ鍵を 420 文字で返した。
CAA —— TLS発行を壊しうる唯一のレコード
CAA(RFC 8659)は、ドメインに対してパブリック認証局のうちどれが証明書を発行してよいかを宣言する。準拠するCAは発行前にCAAを確認しなければならず、どの issue レコードにも名前のないCAは発行してはならない(RFC 8659 §3)。別の発行元を使うCDNやCAへ移行する際にCAAの更新を忘れると、次の証明書更新が失敗する——これは他のどのDNSクエリからも見えない。CAAルックアップだけが、制限が掛かっていることを教えてくれる。
Let’s Encrypt と Google Trust Services(pki.goog)に発行を許可し、ワイルドカード証明書を禁止するゾーンは、こうなる:
example.com. 0 issue "letsencrypt.org"
example.com. 0 issue "pki.goog"
example.com. 0 issuewild ";"
example.com. 0 iodef "mailto:security@example.com"
issuewild ";" は全ワイルドカード発行を拒否する。iodef は、未承認の発行元から要求を受けた場合にCAがレポートをメール送付する先を伝える。ツールのCAAクエリは、各レコードをそのタグ(issue、issuewild、iodef)と値とともに報告するので、発行をトリガーする前にポリシーを確認できる。
落とし穴
プライオリティ 0 でターゲットが . のMX。 RFC 7505 は「Null MX」レコード——0 .——を定義しており、「このドメインはメールを一切受け付けない」を意味する。メールを処理すると思っていたドメインに対してツールが 0 . を返したなら、レジストラかDNSプロバイダがこのプレースホルダを明示的に公開している。修正には、伝播待ちではなく、ゾーンの編集が必要だ。
ゾーン頂点でのCNAMEは違法。 CNAMEを持つ名前はほかのレコードを持てない(RFC 1034 §3.6.2、RFC 1912 §2.4)。一方、ゾーン頂点(example.com)にはSOAとNSが必要だ。Cloudflare(CNAME フラット化)と Route 53(エイリアスレコード)は、頂点でのCNAMEのように見えて、クエリ時には A/AAAA で応答するレコードを提供している。こうしたゾーンの頂点をDoHで照会すると、CNAMEではなくフラット化された A/AAAA が返ってくる。これは正しい挙動だが、ダッシュボードで設定したCNAMEが見えると期待した人は混乱しうる。
プライベートリゾルバとPi-hole。 Pi-hole、NextDNS、AdGuard Home、その他DNSベースのコンテンツフィルタが動いているネットワークでは、ブラウザやOSの通常の名前解決はそのフィルタを通り、応答が書き換えられたりブロックされたりする可能性がある。ツールの照会は HTTPS でパブリックリゾルバに届くので、フィルタには照会した名前が見えない。ただしブラウザは cloudflare-dns.com や dns.google 自体をローカルのDNSで解決するので、フィルタがこの2つの名前をブロックすることはできる。ラップトップの通常の名前解決が失敗するときにツールが本当の答えを返せるのはこのためだ。これは役に立つ場合(誤設定されたPi-holeルールのデバッグ)もあれば、誤解を招く場合(ネットワークがブロックしているドメインを、ツールは「動く」と報告してしまう)もある。
EDNS Client Subnet と CDN テスト。 CDNのジオルーティングをテストする際、DoHが返すIPはCloudflareかGoogleから見える景色であって、あなたから見える景色ではない。特定のリージョンからのテストには、DNS Checker のような複数地域から照会するツールを使うか、そのリージョン内のクラウドVMで dig を走らせる必要がある。ZeroToolのツールが答えるのは「このパブリックリゾルバは何を返すか?」であって、「サンパウロのユーザーから見てどうか?」ではない。
DNSSEC失敗はサーバー障害に似て見える。 あるゾーンがDNSSECチェーンを誤って署名している場合、CloudflareとGoogleはどちらも AD=false 付きのSERVFAILを返す。これはリゾルバがゾーンにまったく到達できないときと同じ応答コードだ。レコード欄の下のリゾルバ注記で両者を区別できる。dnssec-failed.org では、Cloudflare の注記は EDE(9): DNSKEY Missing ...(RFC 8914 の拡張エラー 9)だ。Google は「DNSSEC validation failure」で始まる説明文に加えて extended_dns_errors フィールド(コード 9)を返し、ツールは後者を EDE(9): No DNSKEY matches DS RRs of dnssec-failed.org と表示する。どちらのリゾルバでもピルは「DNSSEC failed」になる。ツールは常に cd=0 を送る——これは日常的なルックアップで正しいデフォルトだ。検証前の答えを見るには dig +cd を、チェーン全体の分析には DNSViz を使おう。
DNSルックアップツールの中での立ち位置
以下のツールは、それぞれ違う問いに答える。
DNS Checker は自社のサーバーから「世界の複数地域にある選ばれた DNS サーバー」に照会し、伝播マップを描く。「この変更はもう全リージョンに届いたか?」という問いに答えるツールだ。
Google Admin Toolbox Dig は照会を自社のバックエンド(/apps/dig/lookup)に送る。2026-09-29 のテストでは、dnssec-failed.org に対して「Record not found!」と表示し、rcode SERVFAIL は Raw 表示にしか出なかった。署名済みの example.com のフラグ行は QR RD RA で、AD はなかった。
dig と kdig は CLI のリファレンス実装であり、スクリプト用途や、権威サーバー指定が必要なクエリ(dig @ns1.example.com)にとっての正解だ。
ZeroToolのツールはその中間ケースを埋める——dig をインストールするには Homebrew パッケージか WSL ディストロを追加することになるようなマシンで、ブラウザから手早く照会したい、しかも照会サイトのサーバーを経由した結果ではなくパブリックリゾルバ自身の答えを見たい、という開発者向けだ。Cloudflare と Google を切り替えて比べられ、AD フラグとリゾルバの注記が見え、生の JSON をコピーできる。伝播マップや権威サーバー限定クエリには向かない——それぞれ上記のツールの方が優れている。
さらに読む
社内向け:
- HTTPヘッダーアナライザ —— DNSが解決した後、サーバーが何を返すか。
- SSL証明書デコーダ —— 解決されたIPで提供されている証明書がホスト名と一致するかを確認する。
- URLパーサー —— ホストを解決する前に、URLをプロトコル、ホスト、ポート、パス、クエリに分解する。
- IPサブネット計算機 —— A/AAAAが得られた後、ファイアウォール規則用にサブネット構造を導出する。
外部:
- RFC 8484 — DNS Queries over HTTPS —— DoH 標準。
- Cloudflare DoH ドキュメント —— エンドポイント、パラメータ、JSONプロファイル。
- Google Public DNS DoH ——
dns.google/resolveに関する Google のドキュメント。 - RFC 1035 — Domain Names —— DNSの原典仕様。レコード形式についての最も有用なリファレンス。
- RFC 7208 — Sender Policy Framework —— SPFと、TXTセグメンテーションのルール。
- RFC 8659 — CAA —— Certification Authority Authorization。
- RFC 6891 — EDNS(0) —— EDNS Client Subnet が乗っている拡張機構。