URL
URL ・ ゆーあーるえる
Web 上のリソースの場所を表す文字列。スキーム・ホスト・パス・クエリ・フラグメントからなる。
✦ 30秒で試す — 航海猫という1文字が、アドレスの中では9文字に化けるサーバの記録に残る、ページの指定は?予想してみる概要
URL(Uniform Resource Locator)は、Web 上にあるリソースの「住所」を表す文字列です。https://example.com/search?q=cat#results のような1行の中に、「どうやって(スキーム)」「どこの(ホスト名)」「何を(パス)」「どんな条件で(クエリ)」「ページのどこを(フラグメント)」が詰め込まれています。ブラウザのアドレスバーに毎日入力しているものですが、その1行が通信のどの段階で・誰に使われるのかを分解して見ると、Web の動きが立体的に見えてきます。
日常の開発でも URL は至るところに登場します。リンクを張るとき、API を呼び出すとき、リダイレクトを設計するとき、ページのルーティングを決めるとき。「この情報はパスに載せるべきか、クエリに載せるべきか」という判断は、Web アプリケーション設計の最初の一歩です。
よく似た言葉に URI(Uniform Resource Identifier)があります。URI は「リソースを識別する文字列」全般を指す広い概念で、URL はそのうち「場所を示して到達できるもの」を指します。実務ではほとんどの場面で URL と呼んで差し支えありません。
なぜ生まれたか
Web 以前のインターネットには、FTP・Gopher・ニュースグループなど複数のサービスが並立していて、それぞれ「どのサーバの、どのファイルか」を指定する流儀がバラバラでした。ある文書から別のサービス上の文書を指し示す共通の方法がなく、「世界中の文書をリンクで結ぶ」というハイパーテキストの構想を実現できなかったのです。
ティム・バーナーズ=リーが Web を設計したとき、この問題を解くために考えたのが URL です。「スキーム + ホスト + パス」という統一した書式を決めたことで、種類の違うリソースを1つの記法で指せるようになり、あらゆる文書が互いにリンクし合う Web が成立しました(のちに RFC 1738、現在は RFC 3986 と WHATWG の URL Standard が定めています)。URL は HTML・HTTP と並ぶ、Web を支える3つの発明の1つです。
詳細
構造を分解する
https://user:pass@example.com:443/search?q=cat#results を左から順に見ていきます。
スキーム(https)はリソースへの到達方法で、http / https のほか mailto: や ftp: などがあります。HTTPS を選べば通信が暗号化されます。ホスト名(example.com)はリソースを持つサーバの名前で、DNS によって IPアドレスに変換されます。ポート(:443)はサーバのどの窓口に接続するかで、ポート番号にはスキームごとの既定値(http は 80、https は 443)があるので、ふつうは省略されます。
パス(/search)はサーバの中でのリソースの位置で、どのプログラムやファイルが応答するかをサーバが決める手がかりです。クエリ(?q=cat)は キー=値 の組を & でつないだ追加の条件で、検索語やページ番号のように「同じリソースの絞り込み・変化形」を表します。フラグメント(#results)はページの中の位置です。ここだけはサーバに送られず、ブラウザがページを表示したあとにスクロール位置などとして使います。
user:pass@ の部分(ユーザー情報)は、URL に ID とパスワードを埋め込む古い書き方です。履歴やログにパスワードがそのまま残るうえ、https://bank.example.com@evil.example/ のように、本物のドメイン名に見える文字列を @ の前に置くフィッシングに悪用されてきました(この URL の行き先は evil.example です)。仕様でも非推奨とされ、ブラウザは警告を出したり、埋め込みのリソースでは遮断したりします。
各パーツがどこで消費されるかを図にすると、次のようになります。
1行の URL が通信で果たす役割
ブラウザに URL を入力してからページが表示されるまで、URL の各部分は別々の場面で消費されます。
ホスト名は「どのサーバにつなぐか」を決めるために DNS が使い、スキームとポートは接続の方法(TLS を使うか、何番に接続するか)を決めます。パスとクエリは接続したあとに HTTP のリクエスト行 GET /search?q=cat HTTP/1.1 としてサーバへ送られ、ホスト名も Host ヘッダとして一緒に届きます。フラグメントは最後までブラウザの手元に残ります。「URL のどの部分が誰に届くのか」を知っておくと、「フラグメントに入れた値はサーバのアクセスログに残らない」「クエリに入れた値は残る」といった挙動が自然に理解できます。
下の部品で、URL の小包を DNS とサーバへ運んでみてください。各段でどのパーツが剥がれて使われ、何が手元に残るかが見えます。日本語を含む URL や、@ を含む URL も試せます(分解にはブラウザ標準の URL API を使っています)。
金の輪のURL の小包をつかんで、まず名前を IP アドレスに変えてくれる相手へ運んでください。タップするとおまかせで進みます。上の欄で URL を書き換えたり、例を選んだりもできます。
パーセントエンコーディング — URL の中の文字は UTF-8 のバイト列
URL にそのまま書ける文字は、英数字と一部の記号に限られます。日本語やスペース、区切りの意味を持つ & ? # などを値として含めたいときは、パーセントエンコーディングを使います。これは文字を文字コードの UTF-8 でバイト列に変換し、1バイトずつ % と16進数2桁で書く方式です。猫 は UTF-8 で E7 8C AB の3バイトなので %E7%8C%AB になります。文字数ではなくバイト数だけ %XX が並ぶので、日本語の URL が急に長く見えるのはこのためです。
ホスト名の日本語は別の仕組みです。例え.jp のような国際化ドメイン名は、DNS が扱える英数字の列に変換する Punycode(xn-- で始まる形)で問い合わせます。
実務でよくあるバグは、すでにエンコードした文字列をもう一度エンコードして %25E7… になる「二重エンコード」と、逆にエンコードし忘れて & を含む値でクエリが壊れるケースです。URL を組み立てるときは文字列の連結ではなく、URL や URLSearchParams のような言語・ブラウザ標準の API を使うのが安全です。
URL 設計は API とアプリ設計の入口
REST API の設計では、「リソースをパスで表し、操作は HTTP のメソッドで表す」という考え方が基本になります。/users/42 のようにパスが「モノ」を指し、絞り込みや並べ替えはクエリに載せます。この使い分けが崩れると、キャッシュが効かなくなったり、URL を共有・ブックマークしても同じ結果を再現できなくなったりします。「URL だけで状態を再現できるか」は良い Web アプリの目安のひとつです。SPA では、ブラウザの History API でページを読み込み直さずに URL を書き換え、この再現性を保ちます。
URL は見られ、記録され、共有される
URL は単なる住所以上の意味を持つようになっています。検索エンジンは読みやすいパスを評価し、SNS では URL そのものが共有されます。その裏返しとして、クエリに個人情報やアクセス用の秘密の値を載せると、サーバのアクセスログ、ブラウザの履歴、別のサイトへ移るときの Referer ヘッダなどから漏れるおそれがあります。機密情報は URL に含めず、どうしても載せるなら短時間で失効する使い捨ての値にする、というのが原則です。「URL は他人に見られ、記録され、共有されるもの」という前提で設計することが、セキュリティの面でも重要です。
試問
画面の上の欄に https://example.com/login?token=abc123 という URL が入っていて、書き換えることができます。欄の下では、この URL が「小包」になり、つかんで DNS(名前を IP アドレスに変える相手)とサーバへ運ぶと、どのパーツが誰に使われ、何が手元に残るかが見えます。サーバのアクセスログに残したくない値 token=abc123 を持ったまま、ログインのページを開きたい場面です。
金の輪のURL の小包をつかんで、まず名前を IP アドレスに変えてくれる相手へ運んでください。タップするとおまかせで進みます。上の欄で URL を書き換えたり、例を選んだりもできます。
