URL パーサー

WHATWG URL 標準に基づき、任意の URL をプロトコル・ホスト・ポート・パス・クエリ・フラグメントに分解します。クエリパラメータは読みやすい表にデコードされます。

URL をブラウザが見ているとおりの構成要素に分解します。 URL を貼り付けると、ブラウザと同じ WHATWG パーサーで各要素を抽出します。したがって結果は、その URL が実際にどう解釈されるかと完全に一致します。クエリパラメータはデコードして表に整理します。

クエリパラメータ

URL の解剖学

URL の骨格

すべての絶対 URL は同じ骨格に従います。

scheme://user:password@host:port/path?query#fragment

ほとんどの URL はこのうち一部しか使いませんが、文法上はいつでも全部が許されています。URL を読むということは、どこで 1 つの要素が終わり次が始まるかを知ることであり、それを決めるのが ://@:/?# という区切り記号です。

要素ごとの解説

プロトコル(スキーム) はリソースの取得方法を示します(https:mailto:ftp:)。WHATWG パーサーが返す値には末尾のコロンが含まれる点に注意してください。文字列 "https" と比較して戸惑う人が多いところです。

ユーザー名とパスワード は非推奨の userinfo 部です。ブラウザは今も解析しますが、リクエストからは削除し、多くの場合警告を出します。URL に資格情報を埋め込むと、履歴・ログ・リファラヘッダーに漏れるからです。自分が書いたのではない URL にこれらがあれば、フィッシングのサインと考えてください。

ホスト名 はドメインまたは IP のみ。ホスト は既定以外のポートがある場合にポートを含みます。この 2 つのフィールドがしばしば同じに見えるのはそのためです。

ポート はスキームの既定値(https は 443、http は 80)を使う場合は空になります。パーサーが正規化するためで、example.com:443/ はポートが空と報告されます。443 は情報を追加しないからです。

パス は最初の / からクエリの直前までです。http(s) URL で唯一空にならない要素で、オリジンだけの URL でもパスは / になります。

クエリ文字列? から始まりパラメータを運びます。フラグメント# から始まり、サーバーに送信されない唯一の部分です。完全にクライアント側で処理されるため、かつて SPA がルーティングに使い、アクセストークンをフラグメントに置くとサーバーログに残らないのもこの性質によります。

オリジン はスキーム・ホスト・ポートというセキュリティ上重要な 3 点セットです。3 つすべてが一致するときだけ 2 つの URL は同一オリジンであり、同一オリジンポリシーや CORS の判断はまさにこれに基づいています。

パーサーが厳格な理由

WHATWG URL パーサーは絶対 URL しか受け付けません。example.com/page にはスキームがないため単独では解析できません。example.com/page のつもりなのか相対パスなのか、パーサーには判断できないからです。実際のコードは基準 URL に対して相対参照を解決しますが、本ツールは結果に曖昧さが出ないよう意図的に絶対形式を要求しています。

気づく正規化

パーサーは入力文字列をそのまま返しません。スキームとホストを小文字化し、パス中の ... を解決し、既定ポートを削除し、国際化ドメイン名を Punycode に変換します。そのため münchen.de はホスト名 xn--mnchen-3ya.de と報告されます。これは利点です。入力どおりではなく、ネットワークスタックが実際に扱う姿を見せてくれます。

重複するパラメータ

クエリ文字列は ?tag=a&tag=b のように同じキーを繰り返しても構いません。それが何を意味するかを定めた仕様はなく、最初の値を採るフレームワーク、最後の値を採るもの、配列にまとめるものがあります。この表は出現順にすべてを列挙するので、まとめられた後の姿ではなく実際に送られた内容がそのまま見えます。

オープンソースに関する注記:ブラウザ内蔵の WHATWG URL および URLSearchParams API で実装しています。サードパーティライブラリは使用していません。

よくある質問

ポート欄が空なのはなぜですか?
その URL がスキームの既定ポートを使っているからです。WHATWG パーサーは https の 443 と http の 80 を省略します。書いても情報が増えず、オリジンの比較を壊してしまうためです。
プロトコルにコロンが付くのはなぜですか?
URL 標準がそう定めているためです。protocol プロパティの値は 'https' ではなく 'https:' です。裸のスキーム名と比較する場合は末尾のコロンを考慮してください。
host と hostname の違いは?
hostname はドメインのみ、host はポートがあればそれも含みます。https://example.com:8443/ なら hostname は example.com、host は example.com:8443 です。
相対 URL を解析できないのはなぜですか?
/about や ../img.png のような相対参照は基準 URL があって初めて意味を持ちます。結果を曖昧にしないため、パーサーはスキーム付きの絶対 URL を要求します。
フラグメントはサーバーに送られますか?
送られません。# 以降は HTTP リクエストに一切含まれず、ブラウザ内で完結します。OAuth のインプリシットフローがトークンをサーバーログから守るためにこれを使っていたのはそのためです。
Unicode のドメインが変わったのはなぜですか?
国際化ドメイン名は DNS が実際に使う ASCII 形式である Punycode に変換されます。münchen.de は xn--mnchen-3ya.de になります。ブラウザもリクエスト前に同じ変換を行っています。