3バイトを4文字に変換する仕組み
Base64はRFC 4648で規定されたバイナリ⇔テキスト変換方式です。バイナリ列を6ビットずつに区切り、64種類の印字可能ASCII文字(A-Z, a-z, 0-9, +, /)にマッピングします。3バイト(24bit)の入力が4文字(6bit×4)の出力になるため、サイズは ceil(N/3)×4 文字に膨らみます。
膨張率は入力サイズに依存し、3バイト以上では約33%増に収束します。1MBのファイルをエンコードすると約1.33MBになり、MIME(RFC 2045)形式で76文字ごとにCRLFを入れる場合はさらに約2.6%が加算されます。
RFC 4648の4つのバリアント
RFC 4648はBase64だけでなく、Base16・Base32・Base64urlを含む4種類を定義しています。URL/ファイル名で使う場合は + や / が問題になるため、それらを -・_ に置き換えたBase64urlがJWT・OAuth・WebAuthnなどで標準採用されています。
| バリアント | 使用文字 | 1単位 | パディング | 増加率 | 主な用途 |
|---|---|---|---|---|---|
| Base64 (§4) | A-Z a-z 0-9 + / | 6bit | = 必須 | +33% | MIME, Email添付 |
| Base64url (§5) | A-Z a-z 0-9 - _ | 6bit | 省略可 | +33% | JWT, OAuth, URL |
| Base32 (§6) | A-Z 2-7 | 5bit | = 必須 | +60% | 大文字限定環境 |
| Base16 (§8) | 0-9 A-F | 4bit | 不要 | +100% | 16進数表示, ハッシュ値 |
パディングの = は終端境界を示すもので、入力が3の倍数でないとき1〜2個付加されます。RFC 4648 §3.2は省略を明示的に許可しており、Base64urlではパディングなしが標準です。JWTデコーダーで実物を見ると、ヘッダー・ペイロード・署名がすべてBase64url(パディングなし)で連結されているのがわかります。
2026年の標準API: btoa()からUint8Array.toBase64()へ
JavaScriptで長らく使われてきた btoa() / atob() には、コードポイント0xFFを超える文字(日本語・絵文字)を渡すと RangeError で例外になる制限があります。Latin-1(ISO-8859-1)として文字を扱うためです。
2025年9月、TC39提案 arraybuffer-base64 がStage 4に到達し、Uint8Array.prototype.toBase64() / fromBase64() がBaseline 2025(Chrome / Safari / Firefox全対応)として利用可能になりました。Node.jsもv25(V8 14.1)からネイティブ対応しています。ECMAScript 2027仕様への収録が確定しています。
// 新API: alphabetオプションでBase64urlも一発
const bytes = new TextEncoder().encode("こんにちは");
const b64url = bytes.toBase64({ alphabet: "base64url", omitPadding: true });
// デコードはfromBase64
const decoded = Uint8Array.fromBase64(b64url, { alphabet: "base64url" });
new TextDecoder().decode(decoded); // "こんにちは"
| API | Unicode対応 | Base64url | パディング制御 | 状態 |
|---|---|---|---|---|
| btoa() / atob() | ×(Latin-1のみ) | 手動置換が必要 | 不可 | レガシー |
| Uint8Array.toBase64() | ○(バイト列) | alphabetオプション | omitPaddingで制御 | 推奨(Baseline 2025) |
| Buffer.toString('base64') | ○ | 'base64url'指定可 | 自動 | Node.js専用 |
実務でハマる3つの罠
1. JWTを atob() でデコードすると壊れる
JWTはBase64urlでエンコードされているため、+// が -/_ に置換されています。標準 atob() はこれを解釈できず、ヘッダーやペイロードのバイト列が破損します。Base64urlに対応した専用ライブラリか、新APIの { alphabet: "base64url" } オプションを使ってください。
2. パディングを忘れて不正なデコード結果になる
Base64urlのパディング省略文字列を標準デコーダに渡す前に、(4 - str.length % 4) % 4 個の = を補う必要があります。これを忘れると一部ブラウザでサイレントに誤った結果を返します。
3. Data URIで大きな画像を埋め込みLCPが悪化
HTMLに data:image/png;base64,... 形式で大きな画像を埋め込むと、ブラウザはHTMLを高優先度でパースするため他リソースの読み込みをブロックします。DebugBearの計測では、モバイル環境でのData URIは外部リソース参照より最大6倍遅いと報告されています。10KB以上の画像はData URI化を避け、HTTP/2の多重化に任せるのが2026年の定石です。
ブラウザ別の処理性能(2025年計測)
Daniel Lemireが2025年11月にApple M4で計測したベンチマークでは、エンコード速度はChrome/Edge/Braveが19 GB/s、Safariが17 GB/s、Firefoxが2.2 GB/sと、Firefoxが他ブラウザの約1/8のスループットにとどまっています。フロントエンドで大量データをBase64化する処理を組む場合、Firefoxユーザーへの影響を考慮した設計が必要です。