開発者ツール · URL エンコーダーとデコーダー
WHATWG URL 標準と RFC 3986: ブラウザとライブラリが意見を異にする理由
· 背景
URLエンコーディング 規格 開発者ツール
URL には 2 つの生きた定義があり、それらは意図的に一致しません。この投稿では、WHATWG が独自の標準を作成した理由、エンコードと解析における 2 つの違い、およびコードがどちらに従っているかについて説明します。
厳密なライブラリが拒否し、ブラウザが問題なくロードする URL — 1 つの文字列、2 つの判定
JavaScript のバックスラッシュを含む文字列は、ブラウザによって URL パスの一部としてスムーズに解釈される可能性があります。同じ文字列が Python ライブラリ バックエンドに到達しますが、バックスラッシュが許可されていないため、解析が拒否されます。 1 つの URL で 2 つの異なる結果が得られます。どちらも間違っているわけではなく、準拠する基準が異なります。 WHATWG 標準は、不正な入力の処理方法を含め、ブラウザが現実世界の URL に対して実際に何を行うかを説明しています。 RFC 3986 は、URL が理想的に準拠すべき正式な文法を定義しています。 RFC 3986 に基づいて構築された多くのバックエンド ライブラリは、その文法を厳密に適用し、それ以外のものはすべて拒否します。
この相違は、環境間でデータを移動するときに重要になります。ブラウザーが受け入れる URL は、バックエンド ツールでの検証に失敗する可能性があります。コードにどの標準が実装されているかを理解すると、URL がある場所では正常に動作するのに、別の場所では理由もなく異常に失敗するという、架空の問題のデバッグを防ぐことができます。
WHATWG が最初からやり直した理由 — 有効なものではなく、不正な入力に対してブラウザが実際に何を行うかを説明する
WHATWG ワーキング グループは、ブラウザが従わない厳密な正式なルールを定義するのではなく、ブラウザが実際に URL を実際に処理する方法を標準化するために 2004 に設立されました。 RFC 2396 には正式な文法仕様が記載されていますが、実際のブラウザはこれに正確に従ったことはありません。現実世界のブラウザは、スペースの許容、エスケープ文字の処理、RFC が予期していなかった不正な入力からの回復などの実践的なルールを開発しました。
RFC 3986 が 2005 に到着し、整形式の URL と厳格な要件のための正式な文法が追加されました。ブラウザは WHATWG を実装します。バックエンド ライブラリは多くの場合、RFC 3986 を実装します。
エンコード セットと予約文字 — URL 標準のコンポーネントごとのリストと RFC 3986 のカテゴリとの関連性
RFC 3986 は、文字を予約済み、未予約、およびエンコードする必要があるその他すべての 3 つのカテゴリに分類します。コロン、スラッシュ、疑問符、ハッシュなどの予約文字は、URL 内で構造的な意味を持ちます。予約されていない文字は、文字、数字、ハイフン、アンダースコア、ピリオド、チルダです。 these are always safe.それ以外はすべてバイトとしてパーセントエンコードされます。この標準では、キャラクターがどのカテゴリに属するかを把握するという明確なルールが 1 つ提供されています。
WHATWG URL 標準はコンポーネントベースのアプローチを採用しています。グローバル カテゴリを使用する代わりに、スキーム、権限、パス、クエリ、フラグメントの異なるエンコード ルールを個別に指定します。アンパサンドはパス内でエンコードされていても、クエリ文字列内ではそのまま残される場合があります。スペースは常にエンコードされますが、正確な表現はコンテキストによって異なります。このコンポーネントごとの設計はブラウザーの動作とよりよく一致しますが、URL のどの部分をエンコードしているかを把握する必要があります。
エラー許容度: スペース、バックスラッシュ、タブ — 1 つの標準拒否と他の修復を入力します。
どちらの標準でもスペースは %20 になる必要がありますが、ブラウザーはリテラルのスペースを黙って変換します。バックスラッシュはどちらの標準でも禁止されていますが、一部のブラウザではバックスラッシュをパス区切り文字として扱います。タブ、改行、制御文字は禁止されています。 WHATWG は、パーサーの寛容な動作、つまり変換または無視を指定します。
é や Middle などの非 ASCII 文字は、UTF-8 エンコードを使用してパーセント エンコードする必要があります。 RFC 3986 は、実際には文字エンコードのステップ自体を指定しません。バイトが存在することを前提としていますが、テキストからバイトを取得する方法については述べていません。 WHATWG 標準では、明示的に UTF-8 が必要です。まず文字列を UTF-8 バイトに変換してから、パーセント エンコードします。どちらの標準も同じエンコード結果に達しますが、異なる基礎的な前提から出発しており、同じことについて明示的ではありません。
動作した例: 両方のモデルでバックスラッシュとスペースを含む URL を解析 - 出力を比較
文字列「https://example.com/café\ search」の例を見てみましょう。ブラウザはバックスラッシュを検出すると、それをパス文字として認識します。スペースを参照して %20 にエンコードし、https://example.com/café%5C%20search. のようなものを生成します。バックスラッシュとスペースが禁止されているため、RFC 3986 パーサーは URL 全体を即座に拒否します。ブラウザは解析を続けます。厳密なパーサーは完全に停止します。別の例を試してください: "https://user@example.com:80/path?q=a&b=c". どちらの標準も、ユーザー情報、ホスト、ポート、パス、クエリを明確に識別します。この構造化 URL に関しては完全に一致しています。この不一致は、異常または不正な入力の場合にのみ発生します。
URL エンコーダおよびデコーダを開き、RFC 3986 モードとブラウザの動作を比較します。スペース、バックスラッシュ、またはその他の特殊なケースを含む文字列を貼り付けます。このツールは、各標準が同じ入力をどのように異なる方法で変換するかを正確に示します。どちらがより厳格で、それぞれが何を行うのかがすぐにわかります。
ご使用の環境でどれが使用されるか - ブラウザーとノードは URL 標準に従います。多くのサーバー ライブラリは、一般的に説明されている RFC に従っています。
ブラウザでは、JavaScript はデフォルトで WHATWG URL Standard を使用します。 URL API はそれを正確に実装します。 Node.js も WHATWG を使用します。 Python ライブラリは RFC 3986 を実装する傾向があります。 urllib はこれに厳密に従っています。 Java ライブラリはさまざまです。 java.net.URL は RFC 3986 に準拠する傾向があります。 Rust の URL クレートは WHATWG に従っています。 Go の net/url は WHATWG の影響を受けています。これは一般的なパターンであり、絶対的な規則ではありません。
プログラムで URL を構築し、URL がブラウザとバックエンドの間を移動する場合は、標準を 1 つ選択して、それに固執してください。 WHATWG にはブラウザの URL API を使用します。バックエンド ライブラリがより厳密である場合、それは矛盾ではなく、設計上の選択です。
この内容の対象外 — ホスト名の解析、IPv6 リテラル、および IDNA 処理
ホスト名の解析には、URL 解析自体を完全に超える IDNA、punycode、およびレジストラ ルールが関係します。 IPv6 アドレス、mailto: や data: などの特別なスキーム、および空のコンポーネントは、パーセント エンコーディングとは完全に異なる別個のトピックです。ドメインの長さの制限とホスト名の有効性はレジストラによって異なり、この議論には関係ありません。また、相対参照とスキーム固有の解析ルールも除外されます。この投稿では、エンコードと解析の違いのみに焦点を当てます。
この説明では、これらの標準を区別するエンコードと解析の違いに焦点を当てます。ホスト名ルール、DNS ルール、およびスキーム固有の動作を除くと、パーセント エンコーディング ルールに関する混乱が防止されます。
要点: 同じ URL が、ある世界では有効でも、別の世界ではエラーになる — URL エンコーダーとデコーダーがプレーンな RFC 3986 エンコーディングを提供して、ブラウザーがどのように正規化したかを確認できる方法
同じ URL 文字列が、ある標準では有効でも、別の標準では無効になることがあります。どちらも、それぞれの設計目標の範囲内では正しいです。 URL コンポーネントをプログラムでエンコードする場合は、環境に適したツールを使用してください。 WHATWG はブラウザが実際に何を行うかを説明します。 RFC 3986 は正式な文法を定義しています。 URL エンコーダーとデコーダーは、ブラウザーの動作とともに RFC 3986 ルールを表示するため、正確な違いを確認し、状況に適したものを選択できます。
問題は、URL がブラウザとバックエンドの境界を越える場合に最もよく発生します。この違いを理解するということは、偶然や間違いではなく、意図的にその交差点に対処することを意味します。