cv.cm

基礎知識 · テキスト・データ・セキュリティ

URL エンコードとは?

約7分で読めます

結論

URL エンコード(正式にはパーセントエンコーディング)は、URL で使えない文字や特別な意味を持つ文字を、1 バイトごとに % と2桁の16進数に置き換える方式です。スペースは %20、& は %26 になり、ASCII 以外の文字はまず UTF-8 に変換されるので、「é」は %C3%A9 になります。文字列「a b&c」は a%20b%26c にエンコードされます。

URLエンコードを開く

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 になります。標準的なサーバーでは、どちらの形式も同じ値にデコードされます。

よくある間違い

  1. 二重エンコード:エンコード済みのテキストをもう一度エンコードすると、% 自体が %25 にエンコードされるため、%20 が %2520 になります。各値は、URL に組み込むときにちょうど1回だけエンコードしましょう。
  2. URL 全体をコンポーネント用の関数でエンコードする:https:// が https%3A%2F%2F になり、リンクが機能しなくなります。
  3. ルーティング前にデコードする:ルートを照合する前に %2F を / にデコードするサーバーは、だまされて別のパスを読まされることがあります。
  4. 古い文字コードを前提にする:古いシステムの中には「é」を %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 になります。

基礎知識の記事一覧