URLの %E6%9D%B1 のような文字列を読む形へ戻すのがデコードです。反対に、日本語や記号をURLの中へ収めるために変換するのがエンコードです。ただし、URL全体とパラメータの値では扱いが違います。元に戻らないからと何度も変換する前に、どの部分を処理しているかを確認します。
URLエンコード・デコードツールでは、入力した文字列を変換できます。ログイン用トークンや個人情報を含まない短い例で動きを確かめてから、実際の処理へ当てはめる方が良いです。
URLの区切り記号と値を分ける
パーセントエンコードは、バイトを % と16進数2桁で表す仕組みです。UTF-8の「東京」は %E6%9D%B1%E4%BA%AC になります。暗号化ではないため、秘密情報を隠す用途には使えません。
? や & はURLの構造を区切る役割がありますが、検索語そのものに含めたいこともあります。たとえば検索語が「A&B」なら、値の中の & をそのまま入れると別のパラメータとの区切りとして解釈されます。URLに記号を使えないのではなく、置く場所によって変換が必要になります。
検索語: A&B
値をエンコード: A%26B
URL: https://example.com/search?q=A%26B
このURL全体をデコードして q=A&B に書き換え、再びアクセス用に使うと意味が変わります。人が読むための変換結果と、そのまま再利用してよいURLは分けてください。
JavaScriptでは値を一度だけ変換する
値だけを変換するなら encodeURIComponent を使えます。URLを組み立てる場合は、URL と searchParams に未変換の値を渡す方法もあります。
const encoded = encodeURIComponent("東京 A&B");
// %E6%9D%B1%E4%BA%AC%20A%26B
const url = new URL("https://example.com/search");
url.searchParams.set("q", "東京 A&B");
console.log(url.searchParams.get("q"));
// 東京 A&B
searchParams.set へ渡す前に、同じ値を encodeURIComponent で変換する必要はありません。get で取り出した値にも、通常は追加のデコードをしません。URLSearchParamsの処理はWHATWGのURL標準で定義されています。
encodeURI はURLの区切りを残すため、任意のパラメータ値を安全に組み立てる用途には向きません。値に含まれる記号も区切りとして残ることがあります。MDNのencodeURIComponentの説明にある違いを確認し、URL全体へ後から変換をかけるより、部品を分けて組み立てます。
プラス記号が空白になる場合とならない場合
+ は、フォーム形式のクエリを解析するときには空白として扱われます。一方、パス中の + は空白の代用ではありません。プラス記号として使える場所もあるので、一律に空白へ置換すると別のURLになります。
| 入力 | decodeURIComponentの結果 | URLSearchParamsでqの値として読んだ結果 |
|---|---|---|
| A+B | A+B | A B |
| A%20B | A B | A B |
| A%2BB | A+B | A+B |
たとえばフォームから「A+B」という値を送りたいなら、プラス自体を %2B として渡します。URL標準のフォーム解析規則では、プラスを空白へ置換した後にパーセントデコードします。「デコードしたら記号が消えた」場合は、この処理方式の違いを確認してください。
miniToolsのデコード処理は decodeURIComponent です。そのため、入力された + を自動的に空白へは変えません。フォーム形式を調べているのか、一般のパーセントエンコード文字列を調べているのかを区別して結果を読みます。
文字化けやURIErrorは元データまで戻って確認する
decodeURIComponent は、不完全な % 表記や正しいUTF-8にならないバイト列で URIError を返します。MDNの仕様説明に、この二つの条件が示されています。
| 症状 | 確認する箇所 | 最初の対応 |
|---|---|---|
| %E3%81のように途中で終わる | コピー・ログ保存時の欠落 | 元の文字列を取り直す |
| %ZZや単独の%がある | 不正なエスケープか、未変換の文字か | 作成元で値を正しくエンコードする |
| 古い日本語URLだけ失敗する | Shift_JISなどUTF-8以外で作られた可能性 | 作成元の文字コードを確認する |
| 変換は終わるが文字が崩れる | 変換前から壊れていないか | 元データと処理途中を比較する |
例外を無視したり、読めない部分を削ったりしても、元の値を復元したことにはなりません。文字コードが分かった場合は、それに対応した処理でバイト列を文字へ戻します。すでに � などへ置き換わって保存されていれば、その文字列だけでは元に戻せない場合があります。
新しく実装する処理では、入力から保存までの文字コードを揃えます。既存データを一括変換する前には、原文を残したうえで一件だけ照合してください。
%25が残っても繰り返しデコードしない
%25 はパーセント記号自体のエンコードです。二重エンコードの手がかりになりますが、必ず誤りという意味ではありません。
元の値: 東京
1回エンコード: %E6%9D%B1%E4%BA%AC
2回エンコード: %25E6%259D%25B1%25E4%25BA%25AC
この例なら、一度戻すとエンコード済みの文字列が残ります。作成元で二回変換したと確認できれば、もう一段戻す理由があります。ただし、元の値が文字列としての %20 なら、その表現は %2520 で正しいものです。空白になるまで処理すると元の値を壊します。
入力時の値を残し、URLへ入れる直前の値と比べます。次に受信側で取り出した値を確認し、どこで自動変換されているかを調べます。重複した一段を取り除くことが修正であり、「読めるようになるまでデコード」は実装しない方が良いです。
PHPでも受信済みの値を重ねて戻さない
PHPの urldecode はプラスを空白へ変えますが、rawurldecode は変えません。フォーム形式かどうかで選ぶ点はJavaScriptと同じです。urldecodeとrawurldecodeの公式説明で確認できます。
echo urldecode('A+B'); // A B
echo rawurldecode('A+B'); // A+B
PHPの $_GET や $_REQUEST はすでにデコードされています。取り出した値へ無条件に urldecode を重ねると、本来のプラスやパーセント表記を変えてしまいます。
まずは元の文字列を残し、値を取り出す処理を確認してください。URLの転送中にパラメータ自体が消えているなら、文字変換とは別の問題です。HTTPステータス・リダイレクトチェッカーで移動先を調べ、消えた箇所を特定するところから進めます。