URLのパーセントエンコーディングの仕組み

パーセントエンコーディングは、ひとつの具体的な問題を解決するために存在します。URLは限られた予測可能な文字の集まりでなければならないのに、人がそこに入れたいデータはほとんどの場合そうではない、という問題です。

URLに安全に含められる文字は限られている

スペースやアクセント付きの文字、非ラテン文字はURLで安全に使える文字の範囲に含まれておらず、一部の記号はURLの構文の中で特別な意味を持つため、どちらの種類の文字もそのままでは使えず変換が必要になります。

予約文字は構文上の意味を持ち、データとして使う場合はエンコードが必要

&、=、?、#、/といった文字はURLの構造そのものを表しています(クエリパラメータの区切りやフラグメントの指定など)。これらの文字を構造としてではなくデータの一部として値の中に含めたい場合は、URLの構文の一部として誤読されないようパーセントエンコードする必要があります。

非予約文字はエンコードする必要がない

英字、数字、そして一部の記号(-、.、_、~)は特別な構文上の意味を持たず、常に安全に使える文字の範囲に含まれているため、常にそのままURLに使うことができ、パーセントエンコードされることはありません。

%XX形式は16進数で表した1バイト分のエンコード結果

文字をパーセントエンコードするとは、その文字をUTF-8のバイト表現に変換し、各バイトをパーセント記号+16進数2桁で書き表すことです。スペースが%20になるのは、ASCII/UTF-8でスペースのバイト値が0x20だからです。

非ラテン文字はしばしば複数の%XXの組み合わせになる

基本的なASCIIの範囲外にある1文字(韓国語、日本語、中国語やアクセント付きのラテン文字など)は、UTF-8では通常2〜4バイトで表現されるため、2〜4個の%XXの並びになります。これが、非英語の文字を含むエンコード済みのURLが不釣り合いに長く見える理由です。

スペースは文脈によって%20にも+にもなりうる

%20は、URLのどこにあってもスペースを表す汎用的なパーセントエンコーディングです。一方で+記号でスペースを表す方法は、application/x-www-form-urlencodedというフォーム送信専用の古い別の慣習であり、すべてのURLに共通するルールではありません。

パーセントエンコーディングが実際に解決している問題

URLは本来、ブラウザやサーバー、メールクライアントなど数え切れないシステムの間を、確実に、コンパクトなテキストとして行き来できることを目的としており、その多くは歴史的に限られたASCII文字しか安全に扱えませんでした。パーセントエンコーディングは、その安全な範囲の外にある文字と、データとして使われている予約文字を、その文字のUTF-8バイト表現にもとづいた標準的な%XX形式に変換することで、どのシステムが扱っても曖昧さのない、安全にやり取りできる文字列であることを保証しています。

エンコードされたURLを実際に壊してしまうミス

二重エンコード、つまりすでにパーセントエンコードされた文字列にもう一度パーセントエンコードをかけてしまうミスは、実際によくある不具合のひとつです。既存の%記号が%25に変換されてしまい、見た目だけでは気づきにくい形でURLが壊れてしまいます。もうひとつよくある混同は、+と%20をどこでも同じ意味だと思い込むことです。+が「スペース」を意味するのはフォームエンコードされたクエリデータの中だけであり、URLのパス部分などで使うと、スペースではなく文字どおりのプラス記号として誤解されることがあります。

よくある質問

なぜ韓国語や日本語、中国語の1文字をエンコードすると、長いパーセント記号の並びになるのですか?

これらの文字は基本的なASCIIの範囲外にあり、UTF-8では複数のバイト(多くのCJK文字では一般的に3バイト)で表現されるためです。そのバイトひとつひとつが別々の%XXとしてエンコードされるため、短い非ラテン文字の文字列でも、エンコード後にはかなり長い文字列になります。

スペースを表す%20と+の実際の違いは何ですか?

%20はスペースを表す汎用的なパーセントエンコーディングで、URLのどこでも有効です。+でスペースを表すのは、application/x-www-form-urlencoded形式のフォームデータの中だけに当てはまる、より限定的で古い慣習であるため、その文脈以外で+を使うと、スペースではなく文字どおりのプラス記号として解釈されてしまうことがあります。