URLパーサー&デコーダー
任意のURLをそのコンポーネントに解析 · プロトコル、ホスト、ポート、パス、クエリパラメータ、フラグメント。
URLの構造
URL は 6 つの概念部分に解析されます:scheme://userinfo@host:port/path?query#fragment。scheme はクライアントにどのプロトコルを使用するかを伝えます(https、http、ftp、mailto、file、data)、それは常に存在する唯一の部分です。userinfo コンポーネント(username:password@)は現代の使用ではまれです、ブラウザは通常表示された URL から削除します、それは1990年代以来フィッシングベクトルだからです。host はネットワーク場所です、登録ドメイン名、IP アドレス(ドット区切りクワッドの IPv4 または角括弧の IPv6)、または localhost のような特別な名前。port は TCP/UDP ポート(HTTP のデフォルト 80、HTTPS の 443 など)、省略された場合、scheme のデフォルトが適用されます。path はホスト内のリソースを識別するスラッシュ区切りの階層です。query string(? 後のすべて)は & で区切られたキー-値ペアを運びます、フィルタリング、ページネーション、追跡、フォーム送信に使用されます。fragment(# 後のすべて)はサーバーに決して送信されない URL の唯一の部分です、それは特定のセクションへのスクロールのために、または single-page アプリケーションでは、ルート状態を示すためにブラウザによって完全にクライアント側で処理されます。
クエリ文字列形式自体には分岐があります:RFC 3986 ごとにパーセントエンコードされた値を持つ伝統的な ?key=value&key2=value2、+ がスペースを意味する古い application/x-www-form-urlencoded フォームエンコード慣習(元々 HTML フォーム送信用)。ほとんどのパーサーは両方を処理しますが、変換は非対称です:%20 は常にスペースにデコードします、+ はクエリ文字列内でのみスペースにデコードします、パスでは決して。それは本番で最も一般的な URL 解析バグの 1 つです。
URL の簡単な歴史
URL、元々「Universal Document Identifier」、その後「Universal Resource Locator」、は CERN での Tim Berners-Lee の1989年3月の「Information Management: A Proposal」メモ(彼の上司 Mike Sendall が「Vague but exciting」と注釈した)と1991年8月の最初の公開アクセス可能な Web ページとの間に発明されました。最初の標準 URL は1991年8月6日に公開された http://info.cern.ch/hypertext/WWW/TheProject.html でした。1992 年 IETF 議論は語彙の戦いを避けるために UDI を URL に名前変更しました。RFC 1738(「Uniform Resource Locators」)、Berners-Lee、Masinter、McCahill によって起草、は最初の正式な URL 構文として1994年12月に公開されました。RFC 2396 は1998年8月に続き、URL を URI のより広い概念に一般化しました。現在の標準仕様は2005年1月に公開された RFC 3986(「URI Generic Syntax」)で、Berners-Lee、Roy Fielding、Larry Masinter によって編集、Internet Standard STD 66、IETF の最高成熟度レベル。RFC 3986 はあらゆる URL パーサーが名目上ターゲットにするものです。実際には、現代のブラウザは多くのエッジケースで RFC 3986 から逸脱します、それが WHATWG が url.spec.whatwg.org で別個の URL Living Standard をメンテナンスする理由です。WHATWG 仕様は明示的に時間とともに RFC 3986 と RFC 3987 を obsolete にすることを目指しており、2 つは依然として先頭スペースの扱い、パーセントエンコーディングセット、Unicode 正規化のような点で異なります。
予約されていない、予約された、パーセントエンコードされた文字
RFC 3986 §2.3 は予約されていない文字を定義します、パーセントエンコーディングなしであらゆる URI コンポーネントで安全と保証される唯一の文字:A-Z、a-z、0-9、ハイフン(-)、ドット(.)、アンダースコア(_)、チルダ(~)。合計 66 文字。他のすべては、特定のコンポーネントで構造的意味を持つ予約された文字(gen-delims :/?#[]@ と sub-delims !$&'()*+,;=)、または「他の」で URI に現れる場合パーセントエンコードされなければなりません。パーセントエンコーディング(RFC 3986 §2.1)は文字のバイトシーケンス(scheme が他を言わない限り UTF-8)を取り、各バイトを %HH で置き換えます、HH はバイトの 2 桁 16 進値です。したがって UTF-8 でエンコードされた é(バイト 0xC3 0xA9)は %C3%A9 になります、ロシア語 привет は %D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82 になります、文字あたり 2 バイト、6 つの %XX トリプレット、6 つのキリル文字に対する 36 文字のパーセントエンコード URL。
ブラウザは 2 つの方法でパーセントエンコードされたパスを表示します:ほとんどの現代のブラウザ(Chrome、Firefox、Safari)はエンコーディングが有効な UTF-8 のときアドレスバーに元の Unicode グリフをデコードしてレンダリングしますが、ユーザーが URL をコピーするときはリテラルパーセントエンコード形式をコピーします。古いブラウザと多くの Web ログはパーセントエンコード形式のみを表示します、それが「きれいな Unicode URL」が誤解を招く可能性がある理由です:アドレスバーで美しく見え、共有されるあらゆるテキストで醜く見えます。RFC 3987(「Internationalized Resource Identifiers」、IRI)、2005 年 1 月公開、はエンコードされていない形式の Unicode URL を正式化しました、Punycode(RFC 3492、2003年3月)は国際化ドメイン名が DNS のためにラベルごとに ASCII にエンコードされる方法を定義します、中国語のトップレベルラベル 中国 は xn--fiqs8s になり、したがって example.中国 は DNS レベルで example.xn--fiqs8s として解決されます。標準的なデモンストレーションは Wikipedia IRI URL です:https://ja.wikipedia.org/wiki/東京 はあらゆる現代のブラウザで動作します、基礎となるリクエストがパスを /wiki/%E6%9D%B1%E4%BA%AC としてエンコードしてもです。
WHATWG URL Standard、ブラウザが実際に行うこと
IETF の RFC 3986 はあることを言います、ブラウザは少し違うことをします。WHATWG(ブラウザベンダー標準化団体)は url.spec.whatwg.org で別個の URL Living Standard をメンテナンスします、ブラウザが実際に実行するアルゴリズム状態マシンを記述、先頭スペースの扱い、制御文字、コンポーネント間で変動するパーセントエンコーディングセット、Unicode 正規化を含む。WHATWG 仕様はブラウザの URL コンストラクタ(new URL(input))が実装するもので、Node.js、Deno、Bun が組み込み URL 解析のために収束したものです。Ada パーサー、Yagiz Nizipli、Daniel Lemire らによって C++ で書かれた、は Node.js 18.16.0(2023 年 4 月)から Node.js の URL 解析を動かす WHATWG 準拠パーサーになりました、古い url.parse() パスを置き換え、それは以前のあらゆる実装より測定可能に高速で、2026 年の高性能 URL 解析の事実上の標準です。RFC 3986 と WHATWG 仕様は依然として完全には和解しておらず、歴史的な逸脱は依然としてレガシーコードパスと古いランタイムバージョンに現れます。
クエリ文字列、URLSearchParams API
クエリ文字列は技術的に「? 後で # 前のすべて」、仕様は実際にそれをどう解釈するかを定義しません。& セパレータを持つ ?key=value&key=value 慣習は要件ではなく慣習です。実際には、2 つのクエリ文字列形式が支配します:application/x-www-form-urlencoded(デフォルトの HTML フォーム送信形式、+ がスペースを意味する)、標準 URI クエリ慣習(スペースが常に %20)。ブラウザの URLSearchParams API(WHATWG URL Living Standard の一部、約 2017 年からすべての現代のブラウザでベースライン)は両方の形式を解析のために透過的に処理し、stringify するときフォームエンコードバリアントを発行します。繰り返しキーは合法です:?tag=red&tag=blue&tag=green は有効、URLSearchParams.getAll('tag') は ['red', 'blue', 'green'] を返します。異なる Web フレームワークは繰り返しキーケースを異なる方法で扱います、Rails と Express は繰り返しキーを配列に集約しますが、PHP はキーが name[] ブラケット規約を使わない限り後の値で先の値を上書きします、これは API 統合でクロスフレームワークバグの常に源です。
一般的な URL 解析の落とし穴
- 二重エンコーディング。 すでにエンコードされた URL をエンコードすると
%2520を生成します(%自体が%25にエンコードされる)。これは URL が複数のレイヤー(フロントエンド → バックエンド → 分析)を横断し、各レイヤーが「親切に」もう一度エンコードするときに現れます。修正は底までデコードしてから、正確に 1 回再エンコードすることです。 - クエリ文字列の + vs %20。
+はフォームエンコードクエリ文字列でスペースを意味しますが、パスやフラグメントではリテラル+を意味します。慣習を混在させると、「John+Doe」がクエリで「John Doe」になりパスで「John+Doe」のままになる、デバッグが難しいバグを生成します。 - 大文字小文字の区別。 ホストは大文字小文字を区別しません(
EXAMPLE.comとexample.comは同じホスト)、パスはほとんどのサーバー(Linux/Unix)で大文字小文字を区別しますが、他(Windows IIS デフォルト、macOS HFS+)では区別しません。これは同じ URL がサーバーに応じて異なるコンテンツに解決する可能性があることを意味します。 - URL での IPv6。 IPv6 アドレスはホスト:ポートセパレータと衝突するコロンを含みます。解決策は IPv6 アドレスを角括弧で囲むことです:
http://[2001:db8::1]:8080/path。多くの URL パーサーは歴史的にこれで失敗しました、現代のブラウザと WHATWG パーサーはそれを正しく処理します。 - フラグメント内の OAuth トークン。 OAuth 2.0 implicit grant フローはサーバーログに現れないようにアクセストークンを URL フラグメント(
#access_token=...)に返しました。現代の OAuth ガイダンスはこのフローを PKCE 付き authorisation code を支持して非推奨としていますが、レガシーシステムは依然としてフラグメントでトークンを発行します。 - 非ラウンドトリップアイデンティティ。 URL を解析して再 stringify することは常に元の文字列を生成しません、パーサーは正規化します(パーセントエンコードされた予約されていない文字をデコード、ホストを小文字化、いくつかの実装ではクエリパラメータをソート)。
parse(url).toString() === urlと仮定しないでください。
一般的なユースケース
- API リクエストのデバッグ。 長いクエリ文字列を持つ REST エンドポイントは読みにくい、パーサーは各パラメータを独自の行に表示。
- OAuth コールバック検査。 認証フロー URL はサーバーに公開せずにデバッグのためにデコードする必要のある状態、コード、スコープ、アクセストークンエンコードを運びます。
- リダイレクトチェーンの追跡。 URL が複数の中間 URL を通じてリダイレクトする場合、各々を解析することはチェーンを追跡しリダイレクトが壊れる場所を識別するのに役立ちます。
- UTM タグ監査。 分析 URL(
?utm_source=...&utm_medium=...&utm_campaign=...)はクエリ文字列の壁としてではなくパラメータごとに読むのが容易です。 - セキュリティ監査。 URL パラメータでの SQL インジェクションパターンやパストラバーサルシーケンスをスキャン、解析は検査のために各値を別々に公開します。
- フォームエンコードボディの解析。 URL クエリ文字列で使用される同じ形式が
application/x-www-form-urlencodedPOST ボディで使用されます。 - ディープリンク検査。 モバイルアプリディープリンクと Web アプリルートはパスやクエリで複雑な状態をエンコード、解析は構造を可視にします。
- アフィリエイト/共有リンクのレビュー。 メールキャンペーンやアフィリエイトプログラムからの追跡リンクはエンコードされたリダイレクト URL と追跡 ID を運び、デコードから利益を得ます。
プライバシー、URL は実際の秘密を運ぶ
URL は通常秘密として扱われませんが、しばしば秘密のデータを運びます。OAuth コールバック URL はアクセストークンを含みます。マジックリンクログイン URL は使い捨て認証トークンを含みます。パスワードリセットリンクはリセットトークンを含みます。内部 API URL は内部ホスト名とインフラストラクチャを明らかにするルーティングパスを含みます。普通のアプリケーション URL でさえ、クエリパラメータを介してユーザー動作を明らかにします、検索用語、フィルタ選択、プロファイル ID、セッション識別子。Referer ヘッダーはリンクされたあらゆるサイトに前の URL を漏らします、2017 年に W3C Candidate Recommendation として導入された Referrer-Policy ヘッダーによって緩和されました(ブラウザのデフォルトは依然として変動)。URL はサーバーアクセスログ、ブラウザ履歴、ブックマーク、CDN ログ、分析パイプライン、チャットアプリリンクプレビューに到着します。サーバー側 URL パーサーはあなたが貼り付けるすべての URL を見ます、ブラウザのみのパーサーはそうではありません。トークン付き OAuth コールバック、使い捨てトークン付きパスワードリセットリンク、インフラストラクチャを明らかにする内部 API URL、または見知らぬ人のハードドライブにコピーされたくないあらゆる URL には、ブラウザのみのパーサーが正しいアーキテクチャです。解析中に DevTools の Network タブを確認するか、ロード後にページをオフライン(機内モード)にしてください。
よくある質問
URL にはどの部分が含まれていますか?
6 つの概念部分:scheme(https、http、ftp、mailto)、userinfo(現代の使用ではまれ、フィッシング緩和としてブラウザによってほぼ削除)、host(ドメインまたは IP)、port(HTTP デフォルト 80、HTTPS は 443)、path(スラッシュ区切り階層)、query(? 後のキー-値ペア)、fragment(# 後、サーバーに決して送信されない)。完全な文法は RFC 3986 §3(2005 年 1 月、STD 66)と WHATWG URL Living Standard にあります。
URL エンコードされた文字をデコードするには?
パーセントエンコーディングは安全でない文字を % とそれに続くバイトの 16 進コードで置き換えます:スペースは %20、コロンは %3A、スラッシュは %2F、アンパサンドは %26、アットマークは %40。マルチバイト UTF-8 文字はバイトごとにエンコードされます、つまり é は %C3%A9(2 バイト)になります。パーサーは表示出力ですべてのパーセントエンコードされた文字を自動的にデコードします。標準 JavaScript 関数は個々の値をエンコードする encodeURIComponent() とデコードする decodeURIComponent() です。
URL フラグメントとは?
フラグメント(# 後のすべて)は完全にクライアント側で処理される URL の唯一の部分で、HTTP リクエストで Web サーバーに決して送信されません。元の目的:ブラウザをその ID を持つアンカー要素にスクロールする。現代の使用には single-page アプリケーションルート状態(#/dashboard/profile)、OAuth implicit フロートークン(現在は PKCE 付き authorisation code を支持して非推奨)、PDF ページナビゲーション(file.pdf#page=5)が含まれます。フラグメントはサーバーに到達しないので、サーバーログに現れるべきではない値を隠す場所です。
なぜ + は時々スペースを意味し時々 + を意味するのか?
2 つのエンコーディング慣習が存在します。application/x-www-form-urlencoded(デフォルトの HTML フォーム送信形式)はスペースを + にエンコード、標準パーセントエンコーディング(RFC 3986 ごと)はスペースを %20 にエンコード。両方ともクエリ文字列で有効、%20 のみがパスとフラグメントで有効。URLSearchParams は両方を透過的に処理します。クロスコンテキストバグはコードがサーバーがフォームエンコードを期待するクエリパラメータに encodeURIComponent(スペースを %20 にエンコード)を使用するとき、または逆に発生します。
相対 URL を処理しますか?
パーサーは scheme を持つ完全な URL を期待します。/api/users のような相対パスには、ベース URL でプレフィックス(https://example.com/api/users)してパーサー用にしてください。一部の相対 URL 解析(ブラウザが href 属性のためにそうするようにベース URL に対して解決)はロードマップ上、WHATWG の URL コンストラクタの 2 引数形式(new URL(relative, base))はそれを処理し、本番コードが使用すべきものです。
私の URL はどこかに送信されますか?
いいえ。解析は WHATWG の URL コンストラクタを介してブラウザで完全に実行されます、貼り付けた URL はあなたのデバイスを離れません。Parse をクリックする間に DevTools の Network タブを確認するか、ロード後にページをオフライン(機内モード)にしてください。アクセストークンを含む OAuth コールバック URL、使い捨てトークンを含むパスワードリセットリンク、インフラストラクチャを明らかにする内部 API URL、または見知らぬ人のハードドライブにコピーされたくないあらゆる URL に安全です。