パーセントエンコーディングとは
パーセントエンコーディング(percent-encoding/パーセント・エンコーディング)は、バイトを%の後ろの2桁16進数で表す符号化で、URLエンコードの中心となる方式です。ここでいうURLエンコードは成分ごとの変換処理を指します。
たとえば空白を表すバイト20は%20と書けます。日本語などは先にUTF-8の複数バイトへ変わるため、一文字に複数の%HHが並びます。
%の後ろはUnicodeの符号位置ではなく、一個のバイト値です。文字→バイト列→%HH列という二段階を分けて読みます。

%HHは一バイトの表記
URL成分へ入れたい生の文字列を用意します。
通常はUTF-8で一個以上のバイトへ変えます。
00〜FFの値を16進数二桁で表します。
%を先頭に置いた三文字で一個のパーセントエンコード済みバイトになります。
そのまま使える非予約文字
ASCII英数字と-、.、_、~はunreserved(アンリザーブド/非予約)文字です。通常はそのまま使え、パーセントエンコードしても表せますが、同じURLの文字列表現が増えるため、生成側は原則としてそのままにします。
%41とAのような表現を同じ資源と扱うかはURL処理で正規化される場合があります。キャッシュキー、署名、アクセス制御で文字列を直接比較するときは、どの正規形を使うかを決めます。
予約済み文字は文脈で意味が変わる
:、/、?、#などはURL構造を作る予約文字です。区切りとしての/と、パスセグメント内のデータとしての%2Fは、サーバーやルーターの処理によって同じにならない場合があります。
先に復号してからパスを分割するのか、分割後に各セグメントを復号するのかで結果が変わります。プロキシ、framework、アプリケーションが同じ順序で処理するようにします。
16進数のA〜Fは大小文字どちらでもよい
%2fと%2Fの16進数字は同じバイトを表します。生成時は大文字へ統一する実装が多いものの、受信側は両方を同じ値として扱います。
URLの別の部分まで大小文字を同一視できるわけではありません。ホストはASCII大小文字を区別しませんが、パスの大小文字を区別するかはサーバー側の仕組みによります。
不完全な表記は推測で補わない
%だけ、16進数が一桁だけ、16進数でない文字が続く表記は不正です。受信側がそのまま残すかエラーにするかはAPIで異なります。別解釈を勝手に作らず、期待する構文で検証します。
percent-decodeしたバイトが有効なUTF-8になるとも限りません。URL構文としての復号と、バイト列から文字列への復号は別段階で、それぞれエラー処理が必要です。
復号は一度だけ行う
%252eを一度復号すると%2e、二度復号すると.になります。検査層が一度だけ戻し、実行層がもう一度戻すと、隠れていた../、区切り、制御文字などが後から現れます。
外部入力をどの状態で受け取ったかを型や変数名で区別し、復号後にパスや問い合わせの制約を検証します。生のURL、解析済み成分、復号済み文字列を同じ変数で使い回しません。
パーセントエンコーディングは一バイトを%HHで表す規則です。先に文字をバイト化し、URL成分の文脈に必要なバイトだけを変換し、受信側では定めた段階で一度だけ戻します。
percent-encoded byteと成分別encode set:WHATWG URL Standard