Dev Study
← 1行のHTMLが画面に出るまで
レッスン 3 / 8·約 9 分·導線 1行のHTMLが画面に出るまで

名前がアドレスになる — localhost は DNS に行かない

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP今ここ
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. DB保存

地図の2段目ですが、この線では素通りになる規定です。

まず結論

URLから取り出した host 文字列は、そのまま通信の宛先になりません。名前解決の入口で、IPアドレス表記・特別な名前・普通の名前に振り分けられます。

`localhost` は RFC 6761 が予約した特別扱いの対象で、名前解決 API は問い合わせを外へ出さずループバックのアドレスを返すことが求められます(SHOULD であって MUST ではありません)。だからこの線では DNS の段が素通りになる、と規定されています。

? 読む前に予測

`http://localhost:3000/` を開くとき、DNSサーバへの問い合わせは外に出るでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

host 文字列は、まだ宛先ではない

前のレッスン『URLを打つ — :80 が消える理由』で、打ち込んだ文字列が scheme・host・port・path に切り分けられました。取り出せた host は localhost という文字列です。

しかし通信の宛先になれるのはアドレスであって、文字列ではありません。だから host 文字列は次の段で一度、振り分けにかけられます。

  • IPアドレス表記(127.0.0.1 など)— 変換する相手がいないので、そのまま宛先になる
  • 特別な名前 — 規格が答えを固定している名前。localhost がこれ
  • 普通の名前(example.com など)— 名前解決 API に渡され、その先でDNSの委任の木を辿る

この振り分けは名前解決の層で起きます。URL の層ではありません。 取り違えると「URLパーサが localhost をループバックに置き換えている」という誤った像ができます。

具体実物

URL の規格が localhost を特別扱いする唯一の場所

WHATWG URL Standard(Living Standard、2026-09-10 版)の host parser 本体に、"localhost" という文字列は出てきません。特別扱いが現れるのは file スキームの host を読む状態(file host state)だけです。

file://localhost/ を file:/// と同一視する表記の正規化です。一方 http(s) は、同じ規格の例表にこう載っています(下の表は別々の2つの例表から1行ずつ抜き出したもの)。

WHATWG URL Standard の2つの例表から1行ずつ合成(2行目はパース例表で出力が file:/// の行)表
URL host port
https://localhost:8000/search?q=text#hello localhost 8000
file://localhost/ (空文字列) —

http(s) の localhost は、URL の層ではただのドメイン文字列として次に渡されます。振り分けをするのはこの先です。

✓ ここまでの確認

`http://localhost:3000/` の `localhost` を「特別な名前」として扱うのは、どの層の規格ですか。

抽象原理

規格が答えを固定している名前

では誰が localhost をループバックに結びつけているのか。RFC 6761「Special-Use Domain Names」(February 2013)です。§6.3 が localhost. と .localhost に落ちる名前の扱いを関係者ごとに7項目で定めており、ここで関係するのは第1項〜第3項です。

読むべき点が3つあります。

  1. 要求の強さは SHOULD / SHOULD NOT。MUST ではありません。「localhost は DNS に行かない」は強すぎて、正確には「行かないべき、と規定されている」です。
  2. 第3項の対象は "Name resolution APIs and libraries"。DNSの通信そのものではなく手前の呼び出し口です。だから答えは問い合わせが始まる前に固定されるという順序になります。第2項は、さらに手前のアプリ自身の特別扱いも MAY で認めます。
  3. "the respective IP loopback address"。IPv4 と IPv6 のそれぞれのループバックアドレスです。片方だけではありません。
host 文字列がどこで振り分けられるか
URL パーサhost として `localhost` を取り出す○ 文字列のまま次へ
IPアドレス表記か`127.0.0.1` のような表記なら変換不要○ 今回は該当しない
特別な名前かRFC 6761 の localhost names に該当○ ここで答えが固定される
(普通の名前なら)スタブリゾルバ自分では解決できず再帰リゾルバへ渡す× この線では通らない
(普通の名前なら)再帰リゾルバ委任の木を辿って権威サーバへ× この線では通らない
↓
localhost は3段目で答えが決まるので、4段目以降(DNSの問い合わせ)へ進まない
  • ・4段目以降は第25週『名前からアドレスへ — DNSは巨大な委任の木』で扱います
  • ・3段目の要求は SHOULD。従わない実装があっても規格違反とは断定できない
具体実物

実機で、2つの経路を別々に叩く

手元(macOS、Node v24.21.0、DiG 9.10.6)で、名前解決 API を通る経路とDNSを直接叩く経路を別々に試しました。まず前者です。Node の dns.lookup は getaddrinfo を経由します。

$ node -e 'require("dns").lookup("localhost",{all:true},(e,a)=>console.log(a))'コマンド出力
[ { address: '::1', family: 6 }, { address: '127.0.0.1', family: 4 } ]

返ってきたのは2つで、IPv6 のループバックが先頭にあります。同じ環境の hosts ファイルには、ちょうどこの2行が書かれていました。

$ cat /etc/hosts | grep -i localhostコマンド出力
# localhost is used to configure the loopback interface
127.0.0.1 localhost
::1 localhost

この2つが示すのは「hosts ファイルの2行と getaddrinfo の結果が一致した」までです。getaddrinfo がDNSパケットを1つも出していないことの直接証拠は、この環境では取っていません。 RFC 6761 第3項は SHOULD ですから、規格から演繹して断定するのも行きすぎです。

次に、DNSプロトコルを直接叩く側です。

$ dig localhost @8.8.8.8 +noall +comments +answer +authorityコマンド出力
; <<>> DiG 9.10.6 <<>> localhost @8.8.8.8 +noall +comments +answer +authority
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 20639
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; AUTHORITY SECTION:
. 86398 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2026091501 1800 900 604800 86400

status は NXDOMAIN、ANSWER は 0 件、AUTHORITY にルートの SOA が入っています。RFC 6761 第4項はキャッシュDNSサーバにも即時の肯定応答を SHOULD で求めていますが、この応答はそうなっていません。1つの実測例であって、すべてのDNSサーバがこうだという話ではありません。

しかも dig は名前解決 API を経ずDNSを直接叩くので、この出力はブラウザの経路の証拠にならないのです。

✓ ここまでの確認

「localhost は 127.0.0.1 のことです」という説明は、どこが不足していますか。

⇄ 2つを見比べる
具体例 A

localhost(特別な名前)

RFC 6761 §6.3 が、名前解決 API とライブラリに「特別扱いし、アドレス問い合わせには常にループバックアドレスを返す」ことを SHOULD で、キャッシュDNSサーバへ問い合わせを送らないことを SHOULD NOT で求めています。

実測(macOS)経路ごとの結果表
叩いた経路 結果
getaddrinfo (dns.lookup) ::1 と 127.0.0.1 の2つ
/etc/hosts の記述 127.0.0.1 と ::1 の2行
dig localhost @8.8.8.8 NXDOMAIN(ANSWER 0件)
具体例 B

普通の名前とスタブリゾルバ(第25週)

RFC 9499 §6 はスタブリゾルバを "A resolver that cannot perform all resolution itself." と定義し、"Stub resolvers generally depend on a recursive resolver to undertake the actual resolution function." と続けます。その先の委任の木は第25週『名前からアドレスへ — DNSは巨大な委任の木』で扱います。

RFC 9499 §6 の定義(要旨)表
用語 できること
Stub resolver 自分ではすべての解決を行えない
Recursive resolver 再帰モードで動作する
Full-service resolver 再帰モード + キャッシュ

どちらも「名前 → アドレス」を担う仕組みです。それなのに片方は問い合わせが外に出ず、もう片方は外に出ます。この違いを生んでいるのは何でしょうか。

「誰が答えを持つか」ではなく「答えがどの時点で確定するか」を両方で言葉にしてから比べてください。

この章のまとめ

  • host 文字列はまだ宛先ではない。名前解決の入口で IPアドレス表記 / 特別な名前 / 普通の名前に振り分けられる。
  • WHATWG URL Standard が localhost を特別扱いするのは file スキームの host state だけ(file://localhost/ = file:///)。http(s) では普通のドメイン文字列。
  • ループバックに結びつけているのは RFC 6761 §6.3。名前解決 API とライブラリに対する SHOULD / SHOULD NOT であって MUST ではない。
  • RFC 6761 は「IPv4 と IPv6 のそれぞれのループバックアドレス」と書く。実機の getaddrinfo も ::1 と 127.0.0.1 の2つを返した。「localhost = 127.0.0.1 のみ」は不正確。
  • dig は名前解決 API を経ずDNSを直接叩くので、出力はブラウザの経路の証拠にならない。
  • 普通の名前はここでは答えが決まらず、自分ではすべての解決を行えないスタブリゾルバ(RFC 9499 §6)が外へ委ねる。その先は第25週『名前からアドレスへ — DNSは巨大な委任の木』で扱う。

この先の学びにどう繋がるか

アドレスが決まったので、次はそこに接続してバイト列を送る番です。次のレッスン『接続して送る — TCP接続の上を流れるHTTPリクエストのバイト列』で、線の上を流れるものを見ます。

DNS そのものの語彙(委任・キャッシュ・レコード種別)は既存のクイズ群「DNS(名前解決)」に譲ります。この導線コースが追うのは「この線ではどこを通り、どこを通らないか」だけです。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 4正解: 0

RFC 6761 §6.3 が名前解決 API とライブラリに求めることとして、正確なのはどれですか。

用語集(5語)
名前解決 API
名前からアドレスを得るためにアプリが呼ぶOS側の入口。RFC 6761 §6.3 第3項が求める対象。
スタブリゾルバ (stub resolver)
RFC 9499 §6 の定義で「自分ではすべての解決を行えないリゾルバ」。一般に再帰リゾルバに実際の解決を頼む。
再帰リゾルバ / フルサービスリゾルバ
RFC 9499 §6 で、再帰モードで動作するリゾルバ。キャッシュを持つものがフルサービスリゾルバ。RFC 6761 の "configured caching DNS server(s)" がこれ。
ループバックアドレス
自ホスト自身を指すアドレス。RFC 6761 §6.3 は IPv4 と IPv6 の「それぞれのループバックアドレス」と述べる(IPv4 は 127.0.0.1、IPv6 は ::1。この2つの表記は RFC 6761 の逐語ではない)。
NXDOMAIN
問い合わせた名前が存在しないことを示すDNSの否定応答。実測で 8.8.8.8 に localhost を訊くと返った。