Base64 vs URLエンコーディング
Base64エンコーディングとURLエンコーディング(パーセントエンコーディング)は、Web開発において異なる目的を果たします。それぞれをいつ使用すべきかを理解することは不可欠です。どちらもデータを異なる表現に変換するエンコーディング方式ですが、根本的に異なる問題を解決します。Base64は任意のバイナリデータを安全なASCIIテキスト形式に変換するように設計されており、URLエンコーディングはURL内の特殊文字がインターネット上で安全に送信されることを保証します。ユースケースに誤ったエンコーディングを選択すると、データの破損、URLの破損、セキュリティ脆弱性、または不必要に膨張したペイロードサイズにつながる可能性があります。
開発者は、これら2つのエンコーディング方法の間で決定を下さなければならない状況に頻繁に直面します。例えば、URLクエリパラメータに認証トークンを含める場合、Base64エンコーディングとURLエンコーディングのどちらを使用すべきか迷うかもしれません。答えは、データの性質、転送媒体の制約、受信システムの要件によって異なります。このガイドは、適切な選択を行うための包括的な比較を提供します。
URLエンコーディングの理解
URLエンコーディングは、パーセントエンコーディングとも呼ばれ、インターネット上で送信できる形式に文字を変換します。URL仕様(RFC 3986)は、URLで予約なしで許可される文字と、エンコードが必要な文字を定義しています。予約なし文字には、大文字と小文字の英字、数字、およびハイフン(-)、アンダースコア(_)、ピリオド(.)、チルダ(~)が含まれます。これら以外の文字はすべて、URLに現れる場合はパーセントエンコードする必要があります。
スペースは%20になり、特殊文字は%に続く16進数のASCII値に置き換えられます。エンコーディングプロセスは単純です。予約なし文字ではない各バイトは、%とそれに続く2桁の16進数表現に置き換えられます。例えば、文字列hello worldはhello%20worldになり、a&b=cはa%26b%3Dcになります。
URLエンコーディングが必要な理由
URLは、歴史的および実用的な理由から制約された文字セットを持っています。元のURL仕様は、インターネットが主に7ビットASCIIテキストを送信していたときに設計されました。この範囲外の文字、またはURLで特別な意味を持つ文字(?、#、&など)は、URLパーサーによって誤って解釈されるのを防ぐためにエンコードする必要があります。
例えば、&文字はクエリパラメータを区切るために使用されます。データに&が含まれていると、データとしてではなくパラメータセパレータとして解釈されます。URLエンコーディングは&を%26に変換し、パラメータ値の一部として扱われることを保証します。同様に、#文字はURLフラグメントの始まりを示します。%23はデータとして現れることを保証します。
URLエンコーディングはまた、UTF-8エンコーディングを通じて非ASCII文字をURLに含めることを可能にします。例えば、Unicode文字U+00E9(é)はURL内で%C3%A9としてエンコードされます。これにより、国際化ドメイン名とパスをASCIIのみのURL仕様内で表現できます。
一般的なURLエンコード文字
| 文字 | エンコード後 | 文字 | エンコード後 |
|---|---|---|---|
| スペース | %20 | # | %23 |
| ! | %21 | $ | %24 |
| " | %22 | % | %25 |
| & | %26 | + | %2B |
| , | %2C | / | %2F |
| : | %3A | ; | %3B |
| = | %3D | ? | %3F |
スペース文字は特に注目に値します。なぜなら、2つの可能なエンコーディングがあるからです。クエリ文字列では、application/x-www-form-urlencoded仕様はスペースを%20ではなく+としてエンコードします。このレガシーな動作はHTMLフォーム送信に由来します。クエリパラメータのデータをエンコードする場合、フォームエンコーディング規則に従う場合はスペースに+を使用し、パスなどの他のURLコンポーネントではスペースに%20を使用する必要があります。
URLコンポーネント別のURLエンコーディング
URLの異なる部分には異なるエンコーディング要件があります。パスコンポーネントは、パスセグメントを区切るため、/をエンコードすべきではありません。クエリコンポーネントは、クエリ文字列で特別な意味を持つため、?や&をエンコードすべきではありません。ただし、データにこれらの文字が含まれている場合は、エンコードする必要があります:?は%3F、&は%26になります。
フラグメントコンポーネント(#の後)は、フラグメントがサーバーに送信されることはないため、最も緩いエンコーディング規則を持ちます。ただし、クライアント側の解析での曖昧さを避けるために、エンコーディングが推奨されます。
Base64エンコーディングの理解
Base64は、64文字のアルファベットを使用してバイナリデータをASCIIテキストに変換します。アルファベットはA-Z、a-z、0-9、+、/で構成され、パディングには=が使用されます。この64文字セットにより、エンコードされた出力は普遍的に安全なASCII文字のみで構成されますが、+と/の文字はURLで使用する場合、追加のURLエンコーディングが必要です。
Base64エンコーディングは、入力データを3バイト(24ビット)のグループで処理します。これらの24ビットは4つの6ビットグループに分割され、各6ビット値(0-63)はBase64アルファベットの文字にマッピングされます。入力長が3バイトの倍数でない場合、出力長を4文字の倍数にするためにパディング文字(=)が追加されます。
Base64エンコーディングの主な目的は、テキストベースの転送チャネルに対してバイナリデータを安全にすることです。メール(MIME)、JSON、XML、HTTPヘッダーはすべてテキストベースのプロトコルであり、バイナリバイトが制御文字として解釈されたり、転送層によって変更されたりする可能性があるため、生のバイナリデータを確実に処理できません。
主な違い
Base64とURLエンコーディングの基本的な違いは、それぞれの異なる目的と設計上の制約に由来します。
| 特徴 | Base64 | URLエンコーディング |
|---|---|---|
| 目的 | バイナリからテキストへ | URLセーフなテキスト |
| 出力サイズ | 約33%増加 | 可変 |
| 文字セット | A-Z, a-z, 0-9, +, /, = | %に続く16進コード |
| 可逆性 | 可逆 | 可逆 |
| ユースケース | Data URI、メール、API | クエリパラメータ、フォームデータ |
| 入力タイプ | バイナリデータ | 特殊文字を含むテキスト |
Base64は、入力内容に関係なく常にデータを約33%拡大します。これは、3入力バイトごとに4出力文字になるためです。URLエンコーディングはデータを可変量で拡大します。ASCIIの英字と数字はまったく拡大されません(1バイトは1バイトのまま)。スペースは1バイトから3バイト(%20)に拡大されます。UTF-8としてエンコードされたASCII範囲外の文字はさらに拡大されます。単一のUnicode文字が2〜3のUTF-8バイトになり、それぞれが%XXとしてエンコードされ、URL内で6〜9バイトになります。
文字セットも大きく異なります。Base64出力は固定の65文字セットを使用しますが、URLエンコーディングは%XXの形式で任意の文字を生成できます。つまり、Base64出力はバイナリデータに対してよりコンパクトですが、二次エンコーディングなしではアルファベット外の文字を表現できません。URLエンコーディングはより柔軟ですが、バイナリデータに対してはスペース効率が悪くなります。
URLセーフBase64(Base64URL)
標準のBase64はアルファベットの一部として+と/を使用するため、Base64でエンコードされたデータは追加のURLエンコーディングなしではURLで直接使用できません。これに対処するために、Base64URLバリアントが導入されました。Base64URLは+を-に、/を_に置き換え、パディングの=文字を削除します。これらの置換により、パーセントエンコーディングを必要とせずにURLで安全な出力が生成されます。
Base64URLはJWT(JSON Web Token)で使用されており、ヘッダーとペイロードをこのバリアントでエンコードします。eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dQw4w9WgXcQのようなJWTトークンを見た場合、最初の2つのセグメントはBase64URLでエンコードされています。
それぞれを使用すべきタイミング
次の表は、一般的なシナリオのクイックガイダンスを提供します。
| シナリオ | エンコーディング |
|---|---|
| HTMLへの画像埋め込み | Base64 |
| クエリパラメータでのデータ送信 | URLエンコーディング |
| メール添付ファイル | Base64 |
| フォーム送信 | URLエンコーディング |
| API認証トークン | Base64 |
| URL内のファイルパス | URLエンコーディング |
| JWTトークン | Base64URL |
| Cookie値 | URLエンコーディング |
適切なエンコーディングの選択
Base64とURLエンコーディングのどちらを選択するかについては、以下のガイドラインに従ってください。
Base64を使用する場合:
- テキストベースのプロトコルを通じてバイナリデータ(画像、ドキュメント、暗号化データ)を転送する必要がある場合。
- HTML、CSS、JSONにデータをインラインで埋め込む場合(Data URI)。
- MIMEメール添付ファイル用のデータをエンコードする場合。
- 認証トークンやその他の不透明なデータブロブを作成する場合。
URLエンコーディングを使用する場合:
- 特殊文字を含むURLやクエリ文字列を構築する場合。
application/x-www-form-urlencodedを介して送信されたフォームデータを処理する場合。- URLパスセグメント、クエリパラメータ、フラグメントで使用するテキストデータをエンコードする必要がある場合。
- 特殊文字を含む可能性のあるCookie値をエンコードする場合。
エンコーディングの組み合わせ
場合によっては、両方のエンコーディングを一緒に使用する必要があります。例えば、Base64でエンコードされた値をクエリパラメータとして渡す場合、Base64出力をURLエンコードして、+や=の文字がURLで安全であることを確認する必要があります。この二重エンコーディングは、トークンや識別子がBase64エンコードされてクエリパラメータとして送信されるAPI設計で一般的です。
const base64Data = btoa('some binary data');
const urlSafe = encodeURIComponent(base64Data);
// urlSafe is now safe for use in a URL
受信側では、最初にURLデコードし、次にBase64デコードするという逆のプロセスを実行します。
実用的な例
例1:HTMLへの画像の埋め込み
HTMLメールに埋め込みたい1KBのPNGアイコンがあるとします。正しいアプローチはBase64エンコーディングです:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...">
メールHTMLはパーセントエンコードされたData URIをネイティブでサポートしていないため、URLエンコーディングはここでは役に立ちません。
例2:URLでの検索クエリの受け渡し
クエリ「café & bakery」を含む検索リンクを作成したいとします。正しいアプローチはURLエンコーディングです:
https://example.com/search?q=caf%C3%A9+%26+bakery
これにBase64を使用すると、はるかに長く読みにくいURLになります。
例3:API認証トークン
APIがユーザーIDとタイムスタンプを組み合わせたトークンをHMACで署名して使用しているとします。トークンはバイナリであり、クエリパラメータとして送信する必要があります。正しいアプローチは、Base64(できればBase64URL)の後にURLエンコーディングを行うか、転送層が残りの特殊文字を処理する場合は単にBase64URLを使用することです。
https://api.example.com/data?token=eyJ1c2VySWQiOjEyMywidGltZXN0YW1wIjoxNzA0MDAwMDAwfQ
バイナリトークンに直接URLエンコーディングを使用すると、はるかに長い結果になります。