URL ノーマライザー
URL を正規化します。スキームとホストを小文字に、デフォルトポートを削除、クエリパラメータをソート、空のフラグメントを除去
URL
正規化済み
URL ノーマライザーとは?
「同じ」はずの 2 つの URL が、大文字小文字やクエリ順序のせいで等しくないと判定される — そんな半日を溶かすバグの典型です。HTTPS://Example.com/page と https://example.com/page は同じリソースを指しますが、文字列比較では別物になります。ノーマライザーは URL を受け取り、RFC 3986 §6 と WHATWG URL Standard に従って正規形を生成するので、同じ意味の URL は同じ文字列になります。
行う正規化は地味だけど大事なものばかりです。スキームとホストを小文字化(RFC 3986 §6.2.2.1 によれば大文字小文字を区別しない)、デフォルトポートの削除(https では :443、http では :80)、パーセントエンコードされた予約外文字のデコード(%41 → A)、クエリパラメータのキー順アルファベット並べ替え、空フラグメント(後ろに何もない #)の除去、そして . や .. パスセグメントの畳み込みです。出力 JSON には正規化済み URL、元の URL、そして書き換えの内容を一つずつ並べた changes 配列が入っているので、見た目が違う 2 つの URL がなぜ実は同じなのか、根拠が分かります。
すべてはブラウザ内で動作します。標準の URL API と URLSearchParams を使うのでサーバー送信もログ取得もありません。入力がすでに正規形なら changes 配列は空で、それが答えです — やることはありません。サイトマップを公開する前や <link rel="canonical"> を設定する前のサニティチェックに便利です。
URL ノーマライザーの使い方
3 ステップ。それぞれがこのページのボタンに対応します。
URL を貼り付けるかサンプルを読み込む
左パネルに URL を入れます。サンプルを押すと、現実的にぐちゃぐちゃな URL が読み込まれます — スキームとホストが大文字、デフォルトポート付き、クエリ順序がバラバラ、パーセントエンコードされたスペース、空フラグメント。サンプル URL:
HTTPS://API.Shop.Example.com:443/v1/orders/?status=active&customer=Ava%20Chen&page=2#国際化ドメイン名(IDN)は URL コンストラクタによって Punycode に変換されます — それが実際にネット上を流れる形で、URI 正規化のルールに従います。ユーザー情報(user:pass@host)は存在すれば保持されます。
出力を読む
右パネルには 3 つのフィールドを持つ JSON が表示されます:normalized(正規 URL)、original(貼り付けた内容、トリム済み)、そして changes — { rule, from, to } エントリの配列で、すべての書き換えが並びます。changes が空なら、URL はすでに正規形でした。
コピーまたはダウンロード
コピーを押すと JSON がクリップボードに送られ、ダウンロードを押すと .json ファイルとして保存されます。最小化は JSON を 1 行に圧縮します。入力パネルのクリアでやり直せます。
実際にこれを使う場面
キャッシュキーと分析の重複排除
分析ダッシュボードでは Example.com/page と example.com/page が別々の行になります。CDN キャッシュも同じです。入口で正規化すれば重複は消えます。URL 短縮サービスやブックマーク重複排除を作るときも同じ手で、正規化された形をルックアップキーとして保存しましょう。
SEO のための正規 URL
同じページが複数の URL から到達できるとき、検索エンジンは重複コンテンツとしてペナルティを与えます。<link rel="canonical"> タグや sitemap.xml の URL は単一の正規化された形に揃えるべきです。Google の 正規化ガイドがルールを説明しており、本ツールはそれを手早く手作業で適用する近道です。
2 つの URL が等しいか比較する
リダイレクトループ検知器を書いたり webhook をテストしているとき。2 つの URL が「同じ」かは、正規化した形が一致するかで決まります。正規化後に文字列比較するのが正解です — URL.toString() ベースの自前の等価関数はやめましょう。クエリ順を揃えませんし、デフォルトポートも残ります。
保存・表示前の URL クリーンアップ
ユーザーがフォームに HTTPS://www.SHOP.com:443/cart/?b=2&a=1# を貼り付けたとします。それをデータベースに入れたくないし、メールで返すなんてもってのほか。先に正規化してから、きれいな形を保存します。顧客に見せる URL が予測可能になります。
よくある質問
すでに正規形だったらどうなりますか?
normalized と original に同じ URL が表示され、changes 配列は空になります("changes": [])。それが答えです — 書き換えるものはありませんでした。その場合でもページは例外を投げたりエラーを出したりしません。
ルートの末尾スラッシュ削除以外で、パスを触りますか?
ほぼ触りません。. と .. パスセグメントは解決されます(URL コンストラクタが自動でやります — RFC 3986 §5.2.4 では「remove dot segments」と呼ばれます)。末尾スラッシュはパスが / だけのときにのみ削除します。/v1/orders/ は /v1/orders/ のままです。RFC 3986 では末尾スラッシュが意味を持つことがあると述べているからです。サーバーフレームワークによっては別ルートとして扱われます。
クエリパラメータが並べ替えられているのはなぜ?順序が大事なのに。
RFC 3986 ではクエリの順序は意味的に重要ではありません — ?a=1&b=2 と ?b=2&a=1 は URL レベルで等価です。アルファベット順に並べると安定した正規形が得られ、等価な 2 つの URL がバイト単位で一致します。アプリが本当にパラメータ順に依存している場合(依存すべきではないですが、レガシーシステムはそうします)、このノーマライザーはその前提を壊します — 順序を気にするサーバーに送る URL は正規化しないでください。
正規化で %20 が + に変わるのはなぜ?
%20 も + もクエリ文字列内ではスペースを意味します(application/x-www-form-urlencoded ルールに従います。URLSearchParams はシリアライズにこれを使います)。URLSearchParams オブジェクトがクエリを再シリアライズすると + が使われます。標準準拠の URL パーサーから見れば意味的には同一です。サーバーが両者を区別するなら、それはサーバー側のバグです。
café.example のような国際化ドメインはどうなりますか?
URL コンストラクタはホストを Punycode に変換します — caf%C3%A9.example は xn--caf-dma.example になります。これが DNS で実際に送られる形で、RFC 3987 と WHATWG 仕様で定められているものです。詳細を知りたければ Wikipedia の URI 正規化記事が IDN の扱いを解説しています。
認証情報付き URL(user:pass@host)でも安全ですか?
パースは完全にブラウザ内で行われ、どこにも送信されません。userinfo は正規化中に保持されます。そもそも URL に認証情報を入れるべきかは別の話です — 多くのブラウザや HTTP クライアントはセキュリティリスクから userinfo を削除するか警告するようになっており、Authorization ヘッダのほうが基本的に安全です。
重複したクエリパラメータは削除されますか?
いいえ。?tag=red&tag=red は ?tag=red&tag=red のままです。繰り返しキーには意味があり得ます(配列に使う API があります)し、削除すると意味が変わります。並べ替えは安定ソートなので、同じキー内では元の順序が保たれます。
その他の URL・JSON ツール
正規化は一つの操作にすぎません。自然に組み合わせられるツールはこちら: