URLエンコード(パーセントエンコード)は、URLでそのまま使えない文字を % と16進数2桁の並びに置き換える仕組みです。標準は RFC 3986 で定義され、対象の文字を UTF-8 のバイト列に変換してから各バイトを %XX で表します。たとえば「あ」は %E3%81%82、半角スペースは %20 になります。本記事では、URLで使える文字・なぜエンコードが必要か・仕組み・encodeURI と encodeURIComponent の違い・フォームでの扱い・落とし穴を正確に整理します。
encodeURIComponent、URL 全体を軽く整形するだけなら encodeURI を使います。エンコードの単位は文字ではなく UTF-8 のバイトで、各バイトが %XX になる、という点を押さえれば大半は理解できます。
1. URLで使える文字 — 予約文字と非予約文字(RFC 3986)
URL(より正確には URI)で使える文字は、ASCII の限られた集合に決められています。RFC 3986 はそれを大きく 非予約文字と予約文字の 2 つに分けています。
- 非予約文字(unreserved):英大小文字
A-Z a-z、数字0-9、および-._~の 4 記号。これらはそのまま使ってよく、エンコードする必要がありません。 - 予約文字(reserved):
: / ? # [ ] @(汎用区切り)と! $ & ' ( ) * + , ; =(サブ区切り)。これらは区切りとして特別な意味を持つため、データとして使いたいときはエンコードが必要です。
上記以外の文字(空白、日本語などの非ASCII文字、制御文字など)は、URLに直接書くことができません。これらを安全に運ぶ手段がパーセントエンコードです。
2. なぜエンコードが必要か — 区切り文字とマルチバイト
エンコードが必要になる理由は、大きく 2 つあります。
- 区切り文字との衝突を防ぐ:URLは
?(クエリの開始)、&(パラメータの区切り)、/(パスの区切り)、#(フラグメント)といった記号で構造を表します。値の中にこれらがそのまま入ると、構造の一部と誤解されます。たとえば検索語がtea&coffeeのとき、?q=tea&coffeeと書くとcoffeeが別パラメータと解釈されてしまいます。 - 非ASCII(マルチバイト)文字を運ぶ:URLが直接表せるのは ASCII の一部だけです。日本語・絵文字・アクセント付き文字などはそのまま置けないため、いったんバイト列に変換してから
%XXで表現する必要があります。
& でも、パラメータの区切りなら生のまま、値の一部なら %26 にエンコードする、という使い分けになります。
3. パーセントエンコードの仕組み — %+16進・UTF-8バイト単位
パーセントエンコードの規則はシンプルです。エンコードしたい文字を UTF-8 でバイト列に変換し、各バイトを % に続けて16進数2桁(大文字推奨)で書きます。
たとえば、半角スペースは1バイト 0x20 なので %20。全角の「あ」は UTF-8 で 3 バイト 0xE3 0x81 0x82 のため、%E3%81%82 と %XX が3つ並びます。
| 文字 | UTF-8 バイト列 | パーセントエンコード |
|---|---|---|
| 半角スペース | 20 | %20 |
& | 26 | %26 |
/ | 2F | %2F |
= | 3D | %3D |
| あ | E3 81 82 | %E3%81%82 |
| café の é | C3 A9 | %C3%A9 |
非予約文字(A-Z a-z 0-9 - . _ ~)はエンコードしてもしなくても同じ意味になるため、通常はエンコードせず残します。
%20 と + は別物です。パス部分での空白は %20 が基本です。+ を空白として扱うのは クエリ文字列やフォーム送信(application/x-www-form-urlencoded)に限った歴史的な約束で、パスでは + はただの + です。混同するとデコード結果がずれます。
4. encodeURI と encodeURIComponent の違い
JavaScript には 2 つのエンコード関数があり、区切り文字を残すかどうかが決定的に違います。
encodeURI:URL 全体をエンコードする想定。: / ? # [ ] @ & = + $ , ;などの区切り文字は残します。すでに組み上がったURLを軽く整形する用途向け。encodeURIComponent:URLの部品(パスの一部・クエリの値など)をエンコードする想定。上記の区切り文字も含めて変換します。値を安全に埋め込むのに使います。
| 入力文字 | encodeURI | encodeURIComponent |
|---|---|---|
/ | そのまま / | %2F |
? | そのまま ? | %3F |
& | そのまま & | %26 |
= | そのまま = | %3D |
| 半角スペース | %20 | %20 |
| あ | %E3%81%82 | %E3%81%82 |
原則は明快です。クエリの値や1個の部品をエンコードするなら encodeURIComponent、組み上がったURL全体を整形するだけなら encodeURI。値に encodeURI を使うと & や = が残り、パラメータが壊れます。
5. クエリ文字列とフォーム送信での扱い
クエリ文字列は ?key1=value1&key2=value2 の形で、& でパラメータを区切り、= でキーと値を結びます。だからこそ、キーと値のそれぞれを encodeURIComponent でエンコードしてから組み立てる必要があります。
- 例:キー
q、値tea & coffeeなら、値をtea%20%26%20coffeeにして?q=tea%20%26%20coffeeとします。 - HTMLフォームを送信すると、ブラウザは
application/x-www-form-urlencoded形式でボディ(または URL)を作ります。これはクエリ文字列と同じkey=value&key=value形式です。 - この形式では歴史的に空白が
+に符号化されます。受け取り側は+を空白に戻したうえで%XXをデコードします。
つまり「パスでは空白は %20、フォーム/クエリの慣習では + もありうる」という二面性を理解しておくと、デコード時の不一致を避けられます。
6. よくある落とし穴
最後に、実務でつまずきやすいポイントをまとめます。
- 二重エンコード:すでにエンコード済みの文字列を、もう一度エンコードしてしまう失敗です。
%20の%自体が%25になり、%2520のような壊れた文字列になります。エンコードは「生の値に1回だけ」が鉄則です。 - 値に
encodeURIを使う:区切り文字が残るため、値に&や=が含まれるとパラメータ境界が崩れます。値には必ずencodeURIComponentを。 - 空白の
+と%20の取り違え:パスで+を空白と誤デコードしたり、フォーム値で%20を期待してずれる、などが起きます。文脈で扱いが違うことに注意。 - 非UTF-8でのエンコード:古いシステムでは Shift_JIS などでバイト化することがあり、UTF-8 前提のデコードと食い違って文字化けします。原則は UTF-8 に統一します。
- 非予約文字まで過剰にエンコード:
-._~はエンコード不要です。過剰に変換しても動きますが、URLが読みにくくなります。
よくある質問(FAQ)
URLエンコードとは何ですか?
URLエンコード(パーセントエンコード)は、URLでそのまま使えない文字を「%」と16進数2桁の並びに置き換える方式です。RFC 3986 で定義されており、対象の文字をいったんUTF-8のバイト列に変換し、各バイトを %XX の形で表します。たとえば全角の「あ」は %E3%81%82、半角スペースは %20 になります。区切り文字(?、&、/ など)と本来のデータを混同させずに安全に送るために使われます。
encodeURI と encodeURIComponent の違いは何ですか?
encodeURI はURL全体をエンコードする想定で、:、/、?、#、&、= などの区切り文字はエンコードせず残します。一方 encodeURIComponent はクエリの値やパスの一部など「部品」をエンコードする想定で、これらの区切り文字も含めて変換します。クエリ文字列の値を組み立てるときは encodeURIComponent を使うのが原則です。encodeURI を値に使うと & や = が残り、パラメータが壊れます。
日本語が %xx の羅列になるのはなぜですか?
URLで直接使える文字はASCIIの一部に限られるため、日本語などの非ASCII文字はそのまま置けません。そこで文字をUTF-8でバイト列に符号化し、各バイトを %XX で表します。日本語の多くは1文字が3バイトのため、1文字あたり %XX が3つ並びます。たとえば「あ」は3バイト(0xE3 0x81 0x82)なので %E3%81%82 となります。