基礎知識 · テキスト・データ・セキュリティ
URL エンコードとは?
結論
URL エンコード(正式にはパーセントエンコーディング)は、URL で使えない文字や特別な意味を持つ文字を、1 バイトごとに % と2桁の16進数に置き換える方式です。スペースは %20、& は %26 になり、ASCII 以外の文字はまず UTF-8 に変換されるので、「é」は %C3%A9 になります。文字列「a b&c」は a%20b%26c にエンコードされます。
URL にエンコードが必要な理由
RFC 3986 は URL に使える文字を ASCII のごく一部に限っており、そのうちいくつかには役割があります。? はクエリの開始、& はパラメーターの区切り、= は名前と値の区切り、# はフラグメントの開始、/ はパスの区切りです。値にこれらの文字が含まれると、URL の意味があいまいになります。
「salt & pepper」を検索する場合を考えます。そのまま ?q=salt & pepper と書くと、サーバーには値が salt のパラメーター q と、 pepper という名前の空の2つ目のパラメーターが届きます。?q=salt%20%26%20pepper とエンコードすれば、1つの値として届きます。
エンコードされる文字
| グループ | 文字 | ルール |
|---|---|---|
| 非予約文字 | A–Z a–z 0–9 - . _ ~ | エンコード不要 |
| 予約文字(gen-delims) | : / ? # [ ] @ | 値の中で使うときはエンコード |
| 予約文字(sub-delims) | ! $ & ' ( ) * + , ; = | 値の中で使うときはエンコード |
| それ以外すべて | スペース、"、<、>、%、ASCII 以外の文字 | 常にエンコード |
エンコードは文字単位ではなくバイト単位で行われます。ASCII 以外の文字はまず UTF-8 に変換され、各バイトがそれぞれ %XX の3文字になります。
| 入力 | UTF-8 のバイト列 | エンコード結果 |
|---|---|---|
| スペース | 20 | %20 |
| & | 26 | %26 |
| % | 25 | %25 |
| é | C3 A9 | %C3%A9 |
| 日本 | E6 97 A5 E6 9C AC | %E6%97%A5%E6%9C%AC |
16進数の大文字と小文字は区別されないので、%c3%a9 と %C3%A9 は同じ意味ですが、RFC 3986 は統一のために大文字を推奨しています。
%20 か + か:スペースの2つのルール
RFC 3986 で定義されたパーセントエンコーディングでは、スペースは常に %20 です。HTML フォームの送信では関連する形式 application/x-www-form-urlencoded が使われ、スペースは +、プラス記号そのものは %2B になります。
/files/my report.pdfのようなパスでは%20を使います。パスの中の+は文字どおりのプラス記号です。- クエリ文字列では、フォーム形式で解析するサーバーは
+をスペースとして読みます。%20はどちらの形式でも通じるので、迷ったらこちらが安全です。 +84のような電話番号など、値に本物の+が含まれる可能性があるなら、%2Bとして送らないとスペースとして届きます。
JavaScript の encodeURIComponent と encodeURI
| 関数 | エンコードしない文字 | 用途 |
|---|---|---|
encodeURIComponent() | A–Z a–z 0–9 - _ . ! ~ * ' ( ) | 1つの値:クエリパラメーター1つ、またはパスの1セグメント |
encodeURI() | 上記に加えて ; , / ? : @ & = + $ # | すでに構造ができあがった URL 全体 |
URLSearchParams | フォーム形式のルール(スペースは + になる) | クエリ文字列の組み立てと読み取り |
encodeURIComponent("a b&c") は a%20b%26c を返しますが、encodeURI("a b&c") は & を区切り文字とみなすため a%20b&c を返します。よくあるバグは、パラメーターの値に encodeURI を使ってしまい、&、=、# がそのまま通って値が分断されることです。
new URLSearchParams({ q: "a b&c" }).toString() は q=a+b%26c になります。標準的なサーバーでは、どちらの形式も同じ値にデコードされます。
よくある間違い
- 二重エンコード:エンコード済みのテキストをもう一度エンコードすると、
%自体が%25にエンコードされるため、%20が%2520になります。各値は、URL に組み込むときにちょうど1回だけエンコードしましょう。 - URL 全体をコンポーネント用の関数でエンコードする:
https://がhttps%3A%2F%2Fになり、リンクが機能しなくなります。 - ルーティング前にデコードする:ルートを照合する前に
%2Fを/にデコードするサーバーは、だまされて別のパスを読まされることがあります。 - 古い文字コードを前提にする:古いシステムの中には「é」を
%E9(Latin-1)とエンコードするものがありました。現代のブラウザーと WHATWG URL Standard は UTF-8 を使います。
よくある質問
URL でスペースが %20 になるのはなぜですか?
URL にはスペースを使えないため、% とそのバイト値の16進数に置き換えます。スペースの ASCII コードは10進数で 32、16進数で 20 です。
URL の %2F は何を意味しますか?
%2F はエンコードされたスラッシュです。値の中に / を入れても、パスの区切りとして読まれないようにできます。
URL エンコードと Base64 は同じものですか?
いいえ。URL エンコードは安全でない文字だけを置き換え、残りは読めるままにします。Base64 はすべてのバイトを 64 文字の文字セットに変換し、標準の + と / 自体も、URL セーフ版を使わない限り URL エンコードが必要です。
アクセント付きの文字や日本語もエンコードする必要がありますか?
実際のリクエストでは、はい。ブラウザーはアドレスバーに読みやすく表示しますが、送信するのは UTF-8 でパーセントエンコードした形で、たとえば「日」は %E6%97%A5 になります。