URLエンコード・デコードツール

「パラメータ」は / ? & = もエスケープします。単一の値に使用してください。「URL全体」はこれらを残すため、URLの構造が保たれます。

入力した内容が保存・記録・共有されることはありません。

テキストをURL用にパーセントエンコード、またはデコードします。単一のパラメータかURL全体かで必要なエスケープが異なるため、モードを選べます。

使い方

  1. エンコードまたはデコードを選びます。
  2. エンコード時は、単一の値なら「パラメータ」、URL全体なら「URL全体」を選びます。
  3. テキストを貼り付けて結果をコピーします。

モードが2つある理由

RFC 3986は、URLに現れる文字を2つのグループに分けています。非予約文字(英字、数字、そして- _ . ~の4つの記号)は常にそれ自体を意味し、エンコードされることはありません。予約文字は構造上の意味を持ちます。スキームの後のコロン、パスの区切りのスラッシュ、クエリの始まりを示す疑問符、パラメーターを区切るアンパサンドとイコール、フラグメントの始まりを示すハッシュです。

2つのモードの違いは、この2番目のグループの扱いだけです。コンポーネントモードは予約文字もエスケープします。1つのクエリ値や1つのパスセグメントなど、テキストが1つの枠の中に入り、構造として解釈されてはならない場合に使います。URL全体モードは予約文字をそのまま残すので、アドレス全体が、スキームやスラッシュ、区切り文字の役割を保ったまま変換されます。

間違ったモードを使うと、たいていすぐにわかります。アドレス全体をコンポーネントモードで変換すると、https://example.comがhttps%3A%2F%2Fexample.comになり、もう開けません。1つの値をURL全体モードで変換すると、値に紛れ込んだアンパサンドがパラメーターを知らないうちに2つに分割してしまいます。

トラブルの原因になる文字

最初に出会うのはスペースです。パスやクエリの中では%20になります。HTMLフォームの送信では、古いapplication/x-www-form-urlencodedの規則により+になり、どちらの慣習も今なお日常的に使われています。デコード時に「+をスペースとして扱う」オプションがあるのはそのためです。フォームのクエリ文字列ならオンに、パスならオフにしてください。パスでは+は文字どおりのプラス記号で、スペースに変えると値が壊れます。

アンパサンドとイコールは値の中ではエスケープしなければならず、そうしないとサーバーがクエリを間違った場所で分割します。検索語や、たまたまそれらを含むBase64の塊が典型例です。ハッシュはそもそもサーバーに送られません。その後ろはすべてブラウザが扱うフラグメントなので、パラメーター内のエスケープされていない#はリクエストを黙って切り詰めます。

値の中のスラッシュには特に注意が必要です。%2Fとパーセントエンコードすれば正当ですが、ルーティングの前にデコードしてしまうサーバーやプロキシが少なくないため、それでもパスの区切りとして読まれることがあります。値にスラッシュが含まれうるなら、パスではなくクエリに入れれば、この問題そのものを避けられます。

二重エンコード

パーセントエンコーディングはべき等ではありません。パーセント記号自体がエンコードの必要な文字なので%25になり、すでにエンコード済みの文字列をもう一度エンコードすると、%20が%2520になります。この並びは、エンコードを1回余計に通ったURLの目に見える痕跡で、一度見覚えるとあちこちで気づくようになります。

アプリケーションのコードで値をエンコードしたうえで、生のテキストを受け取るつもりのフレームワーク、HTTPクライアント、テンプレートがもう一度エンコードすると起こります。修正方法は2つの処理のうち1つを取り除くことで、受け取る側で2回デコードするのはバグの場所を移すだけです。ここで層ごとに1回ずつデコードし、%2520が%20になり、さらにスペースになるかを見れば、実際に何層あったのかがわかります。

非ASCII文字と、パーセントエンコーディングの対象外

テキストはまずUTF-8に変換され、その各バイトがパーセントエンコードされるので、日本語1文字は1つではなく3つのエスケープシーケンスになります。これはURLの標準が求めるとおりの動作で、日本語、中国語、韓国語、アクセント付きのラテン文字、絵文字がここで問題なく往復できるのはそのためです。

ドメイン名は例外で、この違いは重要です。URLのホスト部分はパーセントエンコードしません。国際化ドメイン名はPunycodeで変換され、xn--で始まる名前になります。ホスト名をパーセントエンコードすると、エンコードされたように見えてどこにも名前解決できないものができあがります。このツールが扱うのは、パス、クエリ、フラグメントだけです。

URLの構造

URLは、ほとんどの人が実際に使うよりも多くの部品でできています。スキーム、任意の userinfo、ホスト、任意のポート、パス、クエリ、フラグメントです。すべてを書き出すと https://user:[email protected]:8443/a/b?x=1#top のようになり、これ一本に全要素が含まれています。userinfo の形式は HTTP では非推奨で、ブラウザは取り除くか警告を出します。悪意のあるホストを見慣れたホストのように見せかける手口に使われてきたからです。

部品ごとにルールが違います。ホストは大文字小文字を区別せず、パーセントエンコーディングを含められません。パスは多くのサーバーで大文字小文字を区別します。クエリは「文字列である」こと以上の標準的な文法を持たず、key=value をアンパサンドで区切る書き方は仕様ではなく慣習にすぎません。そしてフラグメントはブラウザの外に出ることがありません。

意外に思われがちなのが相対 URL の解決です。/a/page から b/c へのリンクは /a/b/c に解決されますが、/a/page からだと /a/page/b/c になります。末尾のスラッシュが基準を変えてしまうのです。末尾スラッシュの有無ひとつでページ上の相対パスの参照がすべて壊れるのはこのためで、だからこそ両者の間で正規化のリダイレクトを一度きちんと設定しておく価値があります。

クエリ文字列にあるのは規格ではなく慣習

クエリ文字列で配列を表現する標準は存在しません。だからフレームワークごとに書き方が違います。tags=a&tags=b はキーを繰り返し、tags[]=a&tags[]=b は角括弧を足し、tags=a,b は区切り文字を使います。三つともれっきとした URL であり、受け取る側のライブラリ次第でそれぞれ違うものにパースされます。

入れ子の構造、真偽値、空の値についても同じことが言えます。flag= は false なのか、空文字列なのか、それとも「存在するが値が未設定」なのか。答えは受け取るパーサーが決めることなので、そこを気にする API は挙動をドキュメントに書き、想定に頼らず検証すべきです。

JavaScript では URLSearchParams がエンコーディングと重複キーを正しく扱ってくれるので、文字列を手で組み立てるよりこちらを使うべきです。ただし URLSearchParams はフォームの慣習にならって空白をプラス記号にエンコードし、encodeURIComponent は %20 を生成する点には注意してください。クエリ文字列ではどちらも空白としてデコードされますが、パスでそうなるのは片方だけです。

URLの扱いに潜むセキュリティ問題

オープンリダイレクトとは、パラメータで渡された URL を検証せずに訪問者を送り出してしまうエンドポイントのことです。被害者が信頼しているドメインからフィッシングのリンクを始めるために使われ、セキュリティレビューで今も常連の指摘事項です。対策は遷移先の許可リストを持つこと、あるいは絶対 URL ではなくパスだけを受け付けることです。

サーバーサイドリクエストフォージェリは、同じ種類の問題が内側を向いたものです。サーバーがユーザーの渡した URL を取得しに行き、ユーザーは内部アドレスやクラウドのメタデータエンドポイント、あるいはそこへのリダイレクトを渡します。ここではライブラリごとのパースの差異が繰り返し悪用されてきました。細工された URL がどのホストを指しているかについて、二つのパーサーの解釈が食い違うことがあるからです。

ホモグラフ攻撃は国際化ドメイン名を悪用します。見た目がまったく同じに描画されるキリル文字とラテン文字が、別々のホストになるのです。ブラウザは名前に複数の文字体系が混ざるとピュニコード形式を表示することで身を守ります。怪しいリンクがアドレスバーで xn-- から始まって見えることがあるのはこのためで、この接頭辞は覚えておく価値があります。

正規URL、重複、トラッキングパラメータ

同じページはたいてい複数のアドレスからたどり着けます。www のあり・なし、末尾スラッシュのあり・なし、http と https、index.html の付いた形、そして任意の数のトラッキングパラメータが付いた形です。検索エンジンから見ると、これらは同じ内容を持つ別々のページに見えかねず、そのページが積み上げてきた評価が分散してしまいます。

これを解決する仕組みは二つあります。301 リダイレクトは、構造的なバリエーションのうち一つの形を選び、それ以外をすべてそこへ送ることで解決します。head 内の canonical リンク要素は、リダイレクトできないバリエーション、つまり主にパラメータ違いのケースについて、優先するアドレスを宣言します。

トラッキングパラメータには特に触れておく必要があります。utm_source とその仲間は参照元のシステムが付け足すもので、キャンペーンごとに異なる URL を生み出します。これらは解析ツールの領分であって正規 URL に入れるものではなく、記録が済んだあとにクライアント側で取り除けば、共有されるリンクをきれいに保てます。一部のプラットフォームが自動で付ける fbclid や gclid のパラメータについても同じです。

長く使えるパスを設計する

URL は一種の約束です。他の人がそこへリンクを張り、そのリンクをあなたは制御できないからです。だからこそ、実装ではなくリソースそのものを表すパスにすべきです。/page.php?id=4718 ではなく /guides/subnetting とし、配信技術にアドレスを縛り付ける拡張子は付けないことです。

小文字とハイフンで統一しましょう。サーバー側で大文字小文字が区別されるということは /About と /about が別のページになりうるということで、それが意図した設計であることはまずありません。そしてハイフンはスラッグにおける語の区切りとして慣例になっています。内容が本当にアーカイブでない限り、パスに日付を入れるのは避けましょう。/2024/pricing は、中身が最新であっても一年後には古びて見えます。

URL を変えざるを得ないときは、古い方を恒久的にリダイレクトし、そのリダイレクトをいつまでも残してください。このリストの中で自分の側から取り返しがつかない判断はリンクを壊すことだけです。いちばん重要なリンクは、あなたが名前も聞いたことのないサイトに張られているものだからです。

よくある質問

「パラメータ」と「URL全体」の違いは何ですか?
「パラメータ」はencodeURIComponentを使い、予約文字の / ? & = : もエスケープします。単一の値やパスセグメントに適しています。「URL全体」はencodeURIを使い、これらを残すためURLの構造が保たれます。URL全体を「パラメータ」で処理すると https:// が https%3A%2F%2F になり壊れてしまいます。
クエリパラメータにはどちらを使いますか?
「パラメータ」です。値に & や = が含まれる場合はエスケープしないと、サーバー側で区切り文字と解釈され、意図しない位置で分割されてしまいます。
デコード時にエスケープの形式エラーが出るのはなぜですか?
パーセント記号の後には16進数2桁が必要です。「100%」のような単独の % や、%E4%B8 のように途中で切れた並びは無効なため、推測せずに処理を止めています。
「+ を空白として扱う」はいつ必要ですか?
フォーム送信では空白が %20 ではなく + としてエンコードされます。HTMLフォームのクエリ文字列をデコードする際に有効にしてください。パスセグメントでは + は文字通りのプラス記号なので、オフのままにします。
日本語にも対応していますか?
はい。エンコード前にUTF-8へ変換されるため、日本語・中国語・韓国語・絵文字も正しく往復変換できます。

最終更新:

問題を報告

操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。

関連ツール