パーセントエンコーディング仕様詳解 — RFC 3986とWHATWG URL Standardの違い
URLで使えない文字を「%XX」で表現するパーセントエンコーディングの正規仕様。RFC 3986が定める基本ルールと、Webブラウザの実装基準であるWHATWG URL Standardで微妙に異なる「percent-encode set」の使い分けを整理しました。
1. 仕様の二段構え(RFC 3986 と WHATWG URL Standard)
URIの構文を定める正式仕様はRFC 3986(2005年・Berners-Lee et al.)。一方、Webブラウザの実装基準として現在使われているのはWHATWG URL Living Standardです。両者は概ね同じ方向を向いていますが、後者は「実装されているブラウザの実態」を追記する形で進化しており、percent-encoding の細かい部分で差があります。
2. RFC 3986のunreserved / reserved文字
RFC 3986 Section 2.3 は「unreserved」(エンコード不要)を次のように定義:
- ・ALPHA(A-Z, a-z)
- ・DIGIT(0-9)
- ・
-(ハイフン)・.(ピリオド)・_(アンダースコア)・~(チルダ)
Section 2.2 の「reserved」は2系統:
- ・gen-delims:
: / ? # [ ] @(URLの構造を区切る) - ・sub-delims:
! $ & ' ( ) * + , ; =(コンポーネント内で意味を持つ)
unreservedはエンコードしてもしなくても同じ結果(受信側は復号する)。reservedは文脈によって意味が変わるため、データとして使うときは必ずエンコードします。
3. パーセントエンコーディングの仕様(Section 2.1)
RFC 3986 Section 2.1 は次のように定義:
- ・「%」+ 2桁の16進数(A-Fは大文字・小文字どちらでも受理OK)
- ・大文字・小文字の混在は許容されるが、生成側は大文字を推奨(正規化用)
- ・US-ASCII範囲外の文字は、まず文字をオクテット列(UTF-8など)に符号化してから、各オクテットを%XX形式に
- ・RFC 3986はUTF-8を「強く推奨」(Section 2.5)と明記
4. WHATWG URL Standardの7種類のpercent-encode set
WHATWG URL Standardは、URLの位置(fragment・query・path・userinfo など)ごとにエンコード対象文字の集合を細分化しています。代表的なset:
- ・C0 control percent-encode set:C0制御文字(0x00-0x1F)と0x7Eより大きいコードポイント
- ・fragment percent-encode set:C0 + SPACE, ", <, >, `
- ・query percent-encode set:fragmentと類似だが`を含まず#を含む
- ・special-query percent-encode set:queryに加えて'
- ・path percent-encode set:queryに加えて?, ^, `, {, }
- ・userinfo percent-encode set:pathに加えて/, :, ;, =, @, [, \, ], ^, |
- ・component percent-encode set:userinfoに加えて$, %, &, +, ,
- ・application/x-www-form-urlencoded percent-encode set:componentに加えて!, ', (, ), ~
仕様文(一次情報)では「The application/x-www-form-urlencoded percent-encode set contains all code points, except the ASCII alphanumeric, U+002A (*), U+002D (-), U+002E (.), and U+005F (_).」と明記。HTMLフォームPOSTで送信されるデータはこのsetでエンコードされ、スペースは「+」に置換されます。
5. JavaScriptのencodeURI / encodeURIComponentの対応
ECMA-262仕様では:
- ・
encodeURIComponent⇔ WHATWG URL Standard の component percent-encode set でUTF-8エンコードと等価 - ・
encodeURI⇔ URL全体用:reservedの; / ? : @ & = + $ , #などを残す(クエリの構造を保つため) - ・
decodeURIComponent⇔%XXのすべてを復号 - ・
decodeURI⇔ reservedの;/?:@&=+$,#は復号しない
手軽屋の「URLエンコード・Base64変換」はencodeURIComponentを採用しています。クエリ1つの値・パスの1セグメント分を埋め込むときに安全な側に倒した選択です。
6. 実例:日本語「手軽屋」のエンコード手順
- UTF-8でオクテット列に変換:「手」
E6 89 8B、「軽」E8 BB BD、「屋」E5 B1 8B - 各オクテットを大文字HEXで「%」付きに:
%E6%89%8B%E8%BB%BD%E5%B1%8B - これがGoogle検索「
https://www.google.com/search?q=%E6%89%8B%E8%BB%BD%E5%B1%8B」のクエリ部分の正体
逆向きに、知らないURLの%XX%XX%XX表現を貼り付ければ、ツールが3バイトずつUTF-8デコードして「手軽屋」と表示します。
7. 「+」と「%20」の歴史的経緯
スペース1つを表す方法は2系統:
- ・RFC 3986のパス・クエリ:
%20(オクテット 0x20 = SPACE) - ・application/x-www-form-urlencoded:
+(HTMLフォーム送信専用の特殊表現)
このため、検索フォームで送信したURLのq=foo+barは foo bar の意味、逆にq=foo%20barも同じ意味、というのが「同じスペースなのに表現が違う」混乱の原因。本ツールはデコード時に+も%20として復号する寛容モードで、両方の表現に対応します。
8. 関連ツール・記事
- ・URLエンコード・Base64変換:パーセントエンコーディングの双方向変換
- ・Base64の仕組みとUTF-8対応:RFC 4648の解説
- ・URL文字化けの修復フロー:Shift_JIS時代の遺産を解読
- ・QRコード生成:URLを画像化