
Base64 と Base64URL の違い — RFC 4648 で押さえる3つの落とし穴
Base64 は RFC 4648(2006年)で定義される、バイナリを ASCII 印字可能文字列に変換するためのエンコーディング方式です。実務で最もよく使われるのは JWT、メール添付、Data URL スキームなどですが、URL に含める場面で"+"や"/"が壊れるという落とし穴で初学者が足を取られがちです。本記事では RFC 4648 に規定される標準 Base64 と Base64URL(URL-safe variant)の違いを整理します。
Base64 の基本原理
Base64 は「6ビット単位でバイナリを切り出し、それぞれを64種類の印字可能文字にマップする」という仕組みです。
- 入力: 任意バイト列
- 出力:
A-Z a-z 0-9 + /の64文字 + パディング文字=
3バイト(24ビット)を4文字(24ビット)に変換するため、エンコード後のサイズは元の約 4/3 倍になります。入力が3の倍数でない場合、末尾に 1〜2 個の = でパディングを付けて4文字境界に揃えます。
// 例: "ABC" (3バイト) → "QUJD" (4文字)
// "AB" (2バイト) → "QUI=" (パディング1個)
// "A" (1バイト) → "QQ==" (パディング2個)
RFC 4648 が定める2つのアルファベット
RFC 4648 は Base64 の2種類のアルファベットを規定しています。
| 種別 | 62番目 | 63番目 | パディング | セクション |
|---|---|---|---|---|
| 標準 Base64 | + | / | = | §4 |
| Base64URL | - | _ | 省略可 | §5 |
Base64URL(URL-safe Base64)は、URL やファイル名で使うときに問題となる +(URL では空白扱い)と /(パス区切り)を、URL エンコード不要な - と _ に置き換えたものです。
落とし穴1 — URL パラメータに標準 Base64 を直接入れる
標準 Base64 の結果をそのまま URL パラメータに入れると破綻します。
// NG: "A/B+C=" が URL 上で別の意味になる
https://example.com/api?token=A%2FB%2BC%3D // 正しくは URL エンコード
https://example.com/api?token=A/B+C= // 破綻(+ が空白扱い)
対策: URL に入れる可能性がある場面では最初から Base64URL を使うのが RFC 4648 の意図です。JWT の仕様(RFC 7519)も明示的に Base64URL 使用を指定しています。
// JavaScript での変換例
const std = btoa("Hello/World+"); // 標準: "SGVsbG8vV29ybGQr"
const urlSafe = std.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, ""); // URL-safe: "SGVsbG8vV29ybGQr"
落とし穴2 — パディングの扱い
RFC 4648 §3.2 は、Base64URL においてパディング = を省略してよいと明記しています(実装次第で受け入れるべき)。
JWT では慣例的にパディングを省略します。これにより URL 長が短くなりますが、デコード側が4文字境界を自力で復元する必要がある点に注意が必要です。
// パディング復元のアルゴリズム
function decodeBase64Url(s) {
const pad = (4 - s.length % 4) % 4; // 足りない分を計算
const std = s.replace(/-/g, "+")
.replace(/_/g, "/")
+ "=".repeat(pad);
return atob(std);
}
逆に、標準 Base64 を受け付けるデコーダにパディングなし文字列を渡すと、実装によってはエラーになります。サーバーとクライアントでパディング規則を揃えることがトラブル防止の第一歩です。
落とし穴3 — 改行挿入(MIME Base64 との混同)
歴史的経緯として、RFC 2045(MIME)は Base64 に対して76 文字ごとに CRLF 改行を挿入することを定めていました(Printable-Line 制約のため)。
RFC 4648 は MIME とは別立てで、デフォルトでは改行を挿入しません。ただし、多くのライブラリ(Python 標準 base64.encodestring() や OpenSSL の -base64 フラグなど)が MIME 流の改行挿入を行うため、API に渡す前に改行を除去しないとデコードに失敗します。
// Python で改行なしにしたい場合
import base64
encoded = base64.b64encode(data).decode() // 改行なし ✓
# encodestring() は deprecated, encode() がモダン
# Node.js
const encoded = Buffer.from(data).toString("base64"); // 改行なし ✓
# OpenSSL: -A フラグで改行抑止
openssl base64 -A -in input.bin
RFC 4648 以外のバリアント
関連するエンコーディングとして、同 RFC は以下も定義しています。
- Base32(§6):
A-Z 2-7(I, L, O, S 等の混同しやすい文字を避けている)。TOTP 秘密鍵や Onion アドレスで使用 - Base32 拡張 Hex(§7):
0-9 A-V。DNSSEC で使用 - Base16(§8): 16進エンコーディング。
0-9 A-F。いわゆる hex dump
Base64 系は 4/3 のサイズ効率、Base32 は 8/5、Base16 は 2/1。用途によって可読性と圧縮率のバランスを選びます。
実務での使い分け指針
| 用途 | 推奨 | 根拠 |
|---|---|---|
| メール添付 | 標準 Base64 + 76文字改行 | RFC 2045 MIME |
| HTTP ヘッダー | 標準 Base64 | Basic 認証等の歴史的慣習 |
| URL パラメータ / パス | Base64URL(パディング省略) | RFC 4648 §5 |
| JWT / OAuth | Base64URL(パディング省略) | RFC 7519, RFC 6749 |
| ファイル名 | Base64URL | / が使えないため |
| Data URL スキーム | 標準 Base64 | RFC 2397 |
まとめ
- RFC 4648 は標準 Base64(§4)と Base64URL(§5)の 2種類を定義
- Base64URL は
+/を-_に置換し、パディング=を省略可 - URL/ファイル名/JWT では Base64URL を使う
- MIME Base64(RFC 2045)は 76文字改行を挿入。混在時は除去が必要
- パディング省略ありなしはサーバー/クライアントで合意が必要
- Base32(§6)/ Base16(§8)も同 RFC で定義
参考文献・ソース
記事作成に関する注記
本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。


