URLエンコードで特殊文字を正しく扱う方法
URL全体とクエリ値を区別し、空白、プラス、パーセントなどを二重エンコードせず安全に変換・確認する手順を解説します。
公開日 更新日
URL全体と値を分けて考える
URLにはスキーム、ホスト、パス、クエリ、フラグメントなど、役割の異なる部分があります。URL全体を一つの値としてエンコードすると、https:// の : や /、クエリを始める ? まで変換され、そのまま移動先URLとして使えなくなります。
たとえば検索語 red + blue & 50% を q の値として渡す場合、エンコードする単位は検索語だけです。
input value: red + blue & 50%
encoded value: red%20%2B%20blue%20%26%2050%25
complete URL: https://example.com/search?q=red%20%2B%20blue%20%26%2050%25
既存URLを部品ごとに変更できる環境では、文字列連結よりURL APIやクエリパラメータ用APIを使う方が、どの部分をエンコードするか明確になります。App MuseumのURLエンコード・デコードツールへは、URLへ埋め込む値だけを入力し、「変換の対象」を最初の「URLの部品」のままにするのが基本です。日本語や空白を含むURLを、区切りの記号を残したままリンクとして使える形にしたいときだけ、「URL全体」を選びます。「URL全体」は値の中の &、=、+、# を変換しないので、値に記号を含むURLには使えません。
特殊文字が問題になる理由
空白はパーセントエンコードでは通常 %20 になります。一方、application/x-www-form-urlencoded 形式では空白を + として表すため、元からあるプラス記号は %2B と区別する必要があります。
& はクエリ項目の区切り、= は名前と値の区切り、# はフラグメントの開始として解釈される場合があります。値にこれらを含めるなら、それぞれ %26、%3D、%23 とすることでデータとして保持できます。% は % と2桁の16進数からなるエンコード表記を始めるため、文字として渡す場合は %25 になります。
raw value: plan=a¬e=#1
encoded: plan%3Da%26note%3D%231
よくある失敗は、フォーム形式の + を常にプラス記号としてデコードすること、または値の中の & を未変換のまま連結して別パラメータにしてしまうことです。利用先が通常のURLコンポーネントとフォーム形式のどちらを期待するか確認してください。フォーム形式の場合は、ツールの「変換の対象」で「フォーム形式(空白を + で表す)」を選ぶと、空白を + としてエンコード・デコードできます。
エンコードせずに使える文字
URLでパーセントエンコードが不要な文字は66文字だけです。英字の A〜Z と a〜z、数字の 0〜9、そして記号の -、.、_、~ の4つです。RFC 3986はこれらを非予約文字と呼びます。ハイフンやアンダースコアはエンコード不要で、%5F は同じアンダースコアを長く書いただけの表記です。
注意が要る場合が1つあります。パスの区間が . または .. だけのときは、現在または親のディレクトリを指し、URLの解決時に取り除かれます。%2E と書いても確実には防げません。そのような値はパスに入れず、受け付けないようにしてください。
それ以外の文字は、次の2つのどちらかに入ります。
- 予約文字:
:/?#[]@!$&'()*+,;=。URLに書ける文字ですが、URLの部分や受け取る側のアプリケーションが区切りとして読むことがあります。区切りとして使うときはそのまま書き、値の一部として渡すときはエンコードします。 - そのままでは書けない文字: 空白、
"、<、>、\、^、`、{、|、}、制御文字、そしてéや東のようなASCII以外の文字すべて。必ずエンコードします。%はエンコード表記の先頭としてだけ有効なので、文字としてのパーセント記号もエンコードが必要です。
クエリ値の中のカンマやコロンは予約文字であり、無効な文字ではありません。そのまま書かれたURLも多くありますが、受け取る側がカンマをリストの区切りとして扱うことがあります。%2C と %3A にしておけば迷いがなく、デコードすれば同じ文字に戻ります。
App MuseumのURLエンコード・デコードツールは、「変換の対象」が最初の「URLの部品」のとき、encodeURIComponent に準拠しています。非予約文字に加えて !、'、(、)、* をそのまま残し、それ以外をすべてエンコードします。
input value: my-file_v1.2~draft
encoded value: my-file_v1.2~draft
input value: a,b:c d
encoded value: a%2Cb%3Ac%20d
二重エンコードを見分ける
すでにエンコード済みの %20 をもう一度エンコードすると、% が %25 になり %2520 へ変わります。受信側で一度デコードしても %20 が残り、期待した空白へ戻らないのが典型的な兆候です。
original: a b
once: a%20b
twice: a%2520b
decode once from twice: a%20b
入力が生の文字列か、すでにエンコード済みかを処理境界で決め、同じ値へ複数の層でエンコードをかけないでください。% の後ろに16進数2桁が繰り返し現れるだけでは二重エンコードと断定できないため、元の値と往復結果を比較します。不完全な % や不正なUTF-8列はデコードエラーになることがあります。
encodeとdecodeを往復して確認する
- 実在データではなく、同じ種類の特殊文字を含むテスト値を用意します。
- URLエンコード・デコードツールに値を入力し、「エンコード」を押します。
- 出力を新しい入力として貼り付け、「デコード」を押します。
- デコード結果が最初の文字列と文字単位で一致するか確認します。
- 完成URLでは、値が一つのクエリ項目内に収まり、意図しないフラグメントや追加項目が生じていないか確認します。
東京 + café なら、エンコード後に非ASCII文字と空白と + が変換され、デコード後はアクセント記号を含む元の文字列へ戻ることを確認します。見た目が似るUnicode文字もあるため、必要ならコードポイントや文字列長も確認してください。
秘密情報を使わずにテストする
署名付きURL、パスワードリセットURL、OAuthの認証URL、APIキーやセッショントークンを含むURLは、コピー履歴、画面共有、ログなどから漏れる可能性があります。App Museumの処理はブラウザ内で完結しますが、テストには https://example.com/ と架空の値を使い、実在する秘密情報を貼り付けないでください。
参考資料
このガイドで使えるアプリ

URLエンコード・デコードツール
URLに含める文字列をパーセントエンコード、またはエンコード済み文字列をデコード。: / ? & = を残すURL全体の変換と、空白を + で表すフォーム形式にも対応。ブラウザ内で完結。
