Base64は見たことがあるはずです — SGVsbG8gV29ybGQ=のような長くて不思議な文字列。メール、データURI、APIペイロード、JWTに登場します。でもBase64とは実際何で、いつ使うべきなのでしょうか?謎を解き明かしましょう。

Base64が行うこと(わかりやすく説明)

Base64はあらゆるデータ — テキスト、画像、ファイル、何でも — を、64個の「安全な」ASCII文字だけを使った文字列に変換します:A-Z、a-z、0-9、+、/。末尾の=はパディングです。

なぜ必要なのか?すべてのシステムが生のバイナリデータを扱えるわけではないからです。メールはテキスト用に設計されました。JSONはバイナリをサポートしません。URLパラメータは任意のバイトを含められません。Base64はバイナリデータをテキスト専用チャネルを通して送るための橋渡しなのです。

内部の仕組み

アルゴリズムは驚くほど簡単です。入力3バイト(24ビット)を取り、それぞれ6ビットの4グループに分割し、各グループを64文字のいずれかにマッピングします。簡単な例を見てみましょう:

入力: Hi(2バイト:72, 105)

バイナリ: 01001000 01101001 + パディングゼロ

6ビットグループに分割: 010010 000110 100100

Base64アルファベットにマッピング: SGk=

この計算により、Base64の出力は常に入力の約33%増しになります。これが安全なテキスト伝送のためのトレードオフです。RFC 4648仕様が正確なアルゴリズムを定義しています(詳細が知りたい方はぜひ)。

Base64を使うべき場面

HTML/CSSに小さな画像を埋め込む: ブラウザに別ファイルを取得させる代わりに、小さなアイコンをインラインで埋め込めます:。HTTPリクエストを1つ節約できます。2-3KB以下のアイコンに最適です。

JSON APIでバイナリデータを送信する: JSONは生のバイトを保持できません。JSON APIでファイルをアップロードする必要がありますか?Base64エンコードしましょう:

json

メール添付ファイル: これこそBase64が設計された目的そのものです。MIMEエンコーディングはテキストベースのメールメッセージにバイナリ添付ファイルを埋め込むためにBase64を使用します。

JWTと認証トークン: JSON Web TokenはヘッダーとペイロードセクションにBase64URLエンコーディング(URL安全な変種)を使用します。

テキストフィールドにバイナリを保存: データベースのテキストカラム、環境変数、設定ファイル — Base64を使えば、テキストが使える場所ならどこでもバイナリデータを置けます。

Base64を使うべきでない場面

  • 大きなファイル。 33%のサイズ増加は大きなファイルでは本当に痛いです。10MBの画像はBase64で13.3MBになります。代わりにマルチパートフォームアップロードを使いましょう。
  • セキュリティ。 Base64は暗号化ではありません。簡単に元に戻せます。パスワードや機密データを「隠す」ために絶対に使わないでください。誰でもcGFzc3dvcmQ=を数秒でpasswordにデコードできます。
  • 大きなインライン画像。 約5KB以上の画像には、適切なHTTPキャッシュ付きの別ファイルの方がBase64データURIよりパフォーマンスが良いです。

URL安全な変種

標準Base64は+/を使用しますが、これらはURLで特別な意味を持ちます。Base64URL変種ではこれらが-_に置き換えられます。JWTや、Base64データがURLに含まれるあらゆる場所で見かけるでしょう。

クイックコード例

最も一般的な言語でBase64をエンコード・デコードする方法はこちらです:

javascript
python

JavaScriptのブラウザ関数btoa()はASCII文字列にしか対応していないことに注意してください。Unicodeテキストをエンコードする必要がある場合は、まずTextEncoderを通す追加ステップが必要です — これは多くの開発者がつまずくポイントです。

Base64でよくある間違い

開発者が最もよく陥る落とし穴をご紹介します:

1. Base64が暗号化だと思い込む。 いくら強調しても足りません。APIキーやパスワードをBase64エンコードして「保護されている」と思っているプロダクションコードベースを見たことがあります。ブラウザコンソールがあれば誰でも即座にデコードできます。データを保護する必要があるなら、本物の暗号化(AES、RSA)やハッシュ(bcrypt、argon2)を使いましょう。

2. パディングを正しく処理しない。 一部の実装は末尾の=記号を削除しますが、これは厳密なパーサーでデコードエラーを引き起こす可能性があります。システム間でBase64を送受信する場合、パディングを含めるかどうかを合意しておきましょう。

3. URLで標準Base64を使う。 標準Base64の+/はURLを壊します。クエリパラメータやパスセグメントにエンコードデータを埋め込む場合は、必ずURL安全な変種(Base64URL)を使いましょう。

4. JSONペイロードで大きなファイルをBase64エンコードする。 5MBのファイルはBase64エンコード後にほぼ7MBになり、その文字列全体をメモリに保持する必要があります。数KB以上のものには、代わりにmultipart/form-dataを使いましょう。

Base64文字セットクイックリファレンス

範囲文字
大文字A-Z26
小文字a-z26
数字0-910
記号+ /2
パディング=1
URL安全で置換- _(+ /の代わり)2

実際のユースケース:CSSにフォントを埋め込む

Base64のあまり知られていない一般的な使い方の一つが、カスタムフォントをCSSファイルに直接埋め込むことです。これによりフォントファイルの追加HTTPリクエストを回避できます:

plaintext

このテクニックは小さなアイコンフォント(10-15KB以下)にはうまく機能します。大きなフォントファイルの場合は、ブラウザが独立してキャッシュできるよう、別途配信する方が良いでしょう。

Base64 vs 他のエンコーディング方式

Base64はバイナリデータをテキストとして表現する唯一の方法ではありません。いくつかの代替手段があり、それぞれに最適な場面があります。主なものを比較してみましょう。

16進エンコーディング(Base16): 最もシンプルなアプローチ — 各バイトが2つの16進文字(0-9、A-F)になります。つまりHi4869になります。非常にシンプルで人間が読みやすいですが、出力は入力の100%増し(2倍のサイズ)です。16進はあちこちで見かけます:カラーコード(#FF5733)、SHAハッシュ、MACアドレス、デバッグ出力など。

Base32: 32文字(A-Zと2-7)を使用します。出力は入力の約60%増しです。気づかないうちに見ているかもしれません — Google Authenticatorや他のTOTP/2FAアプリの6桁のコードは、裏でBase32エンコードされたシークレットを使っています。大文字小文字を区別しないので、ユーザーがエンコードされたデータを手動で入力する必要がある場面に最適です。

ASCII85 / Base85: 85個の印刷可能ASCII文字を使用し、出力は入力の約25%増しにしかなりません。これはBase64より効率的です。GitはBase85の変種をバイナリパッチ形式(packfiles)で使用しており、Adobe PostScriptやPDFファイルは内部的にASCII85エンコーディングを使用しています。欠点は?{}、引用符などの文字を使用するため、JSON、XML、URLで頭痛の種になり得ます。

同じ100バイトの入力をエンコードした場合の比較はこちらです:

エンコーディング出力サイズオーバーヘッド使用文字最適な用途
Hex (Base16)200バイト+100%16 (0-9, A-F)デバッグ、ハッシュ
Base32~160バイト+60%32 (A-Z, 2-7)TOTPコード、大文字小文字を区別しない場面
Base64~133バイト+33%64 (A-Z, a-z, 0-9, +/)メール、JSON、データURI
Base85~125バイト+25%85 (印刷可能ASCII)Gitのpackfiles、PDFの内部処理

なぜほとんどの場合Base64が勝つのでしょうか?スイートスポットにいるからです:適度なオーバーヘッド、あらゆる言語とプラットフォームで幅広くサポートされており、一般的なフォーマットで問題を引き起こすほとんどの特殊文字を避けています。Base85はよりコンパクトですが、汎用的なサポートが少なく、構造化されたテキストフォーマットで安全に使うのが難しいです。

認証フローにおけるBase64

Base64の最も広く使われている(そして最も誤解されている)用途の一つが、HTTP Basic認証です。Basic Authでユーザー名とパスワードを送信する際、ブラウザはそれらをコロンで連結し、結果をBase64エンコードします。

ユーザー名admin、パスワードsecret123でログインする際に裏で起きていることはこうです:

plaintext

サーバーはこのヘッダーを受け取り、Base64デコードし、コロンで分割して認証情報を確認します。シンプルですよね?でも重要なポイントがあります:HTTPSなしではこれは安全ではありません。このBase64文字列は簡単に元に戻せます — リクエストを傍受した人なら誰でもYWRtaW46c2VjcmV0MTIzをデコードして1秒以内にパスワードを取得できます。Basic AuthのRFC 7617仕様はこの点について明確に警告しています。

Base64は他の認証コンテキストにも登場します。OAuth 2.0のアクセストークンとリフレッシュトークンはしばしばBase64エンコードされています(ただしこれは実装の詳細であり、要件ではありません)。AWS、Stripe、Google Cloudなどのサービスの多くのAPIキーはBase64エンコードされた文字列です。繰り返しますが、エンコーディングは安全な転送のためであり、セキュリティのためではありません。常にHTTPSで送信し、安全に保管してください。

データURIの詳細

データURIについては先ほど簡単に触れましたが、もっと詳しく見る価値があります。データURIを使えば、ファイルの内容をHTML、CSS、JavaScriptに直接埋め込めます — 外部HTTPリクエストは不要です。完全な構文はこうなります:

plaintext

mediatypeは標準的なMIMEタイプ、;base64フラグはデータがBase64エンコードされていることをブラウザに伝え(これがないとデータはURLエンコードされたテキストと見なされます)、が実際のコンテンツです。

異なるメディアタイプの例をご紹介します:

PNG画像(最も一般的):

html

インラインSVG(Base64不要!):

html

SVGはすでにテキストなので、Base64エンコードする代わりにURLエンコードできることに注目してください。33%のオーバーヘッドをスキップできるので、実際にはより小さくなります。賢い開発者はシンプルなSVGアイコンにこのトリックを使います。

PDFドキュメント:

html

オーディオファイル:

html

ブラウザの制限についてですが、MDNのデータURI仕様は最大サイズを定義していませんが、ブラウザは独自の制限を課しています。Chromeはナビゲーションコンテキスト(iframeやトップレベルページなど)でデータURIを約2MBに制限しています。Firefoxはもう少し寛容です。CSSの背景画像やインライン画像については、実質的な制限はユーザーの忍耐力です — CSSファイルに500KBのBase64文字列を入れることは技術的に有効ですが、読み込み時間を完全に台無しにします。

パフォーマンスへの影響:Base64が害になる場合

実際の数字で話しましょう。「33%のオーバーヘッド」は実際の影響を見るまでは抽象的に聞こえますから。

帯域幅コスト: 50KBの画像はBase64エンコード後に~67KBになります。画像あたり17KBの追加です。ページに20個のインラインアイコンがある場合を掛け算すると、340KBの不要なデータを送信していることになります。参考までに、これはほとんどのウェブページのHTML全体よりも多いです。

メモリ使用量: 画像をBase64エンコードしてHTMLやCSSに埋め込むと、エンコードされた文字列全体がDOMやスタイルシートの一部としてメモリに保持される必要があります。ブラウザはそれをバイナリにデコードするので、一時的にエンコード版とデコード版の両方を保持することになります。1MBの画像の場合、エンコードされた文字列だけで約2.33MB、デコードされたバイナリで1MB — 元のファイルサイズの3倍以上です。

パース時間: HTMLやCSS内のBase64文字列はそれらのドキュメントの一部としてパースされる必要があります。大きなデータURIはCSS全体やHTMLドキュメント全体のパースを遅くします。web.devの画像最適化ガイドによると、インライン画像は一般的にパフォーマンスの純利益を得るためには2-4KB以下であるべきです。

キャッシュの非効率性: これは開発者が最も見落とすポイントです。画像を別ファイルとして配信すれば、ブラウザは独立してキャッシュします — 以降のページ読み込みではダウンロードを完全にスキップします。しかし同じ画像をBase64エンコードしてCSSファイル内に埋め込むと、それはCSSの一部になります。CSS内の他の何かを変更すると、ブラウザは変更されていないBase64画像データを含むファイル全体を再ダウンロードします。粒度の細かいキャッシュを失うのです。

参考までにおおよそのベンチマークです:

シナリオ別ファイルBase64インライン
2KBアイコン追加HTTPリクエスト1つ(HTTP/2で~5ms)HTML/CSSに+0.67KB、追加リクエストなし
50KB画像独立してキャッシュ、HTTP/2マルチプレクスCSSに+17KB、CSSの変更で毎回再ダウンロード
500KB写真キャッシュ可能、遅延読み込み可能、プログレッシブレンダリングドキュメントに+167KB、レンダリングをブロック、遅延読み込み不可

経験則:2-4KB以下のものはBase64インライン化。それ以外はすべて別ファイルとして配信。 HTTP/2やHTTP/3では追加リクエストのコストは最小限なので、この閾値は年々さらに低くなっています。

さまざまなプログラミング言語でのBase64

JavaScriptとPythonは先ほどカバーしました。他の人気言語でBase64を扱う方法はこちら — それぞれたった数行です:

java
go
csharp
php
ruby

どの言語でもBase64が標準ライブラリにあることに注目してください — サードパーティのパッケージは不要です。このエンコーディングがいかに基本的かを示しています。またRubyにはstrict_encode64(改行なし)とencode64(元のMIME標準に合わせて60文字ごとに改行を挿入)があります。現代のほとんどの用途ではstrict版が望ましいです。

Base64の歴史

Base64は突然現れたわけではありません。その歴史はメールの歴史と結びついており、それを理解することで多くの設計上の決定が説明できます。

インターネットの初期の頃、メール(SMTP)は7ビットASCIIテキストのみを扱うように設計されていました — 128文字、それだけです。英語テキストには十分でしたが、画像、ドキュメント、英語以外の言語のテキストを送るには全く役に立ちませんでした。バイナリデータをメールで送ることは単純にできなかったのです。

Base64の最初の正式な仕様は1993年にRFC 1421として登場し、Privacy Enhanced Mail(PEM)標準の一部でした。アイデアはシンプルでした:バイナリデータを、テキストベースのどんな転送システムでも破損なく通過できるASCIIのサブセットに変換することです。

次に1996年にRFC 2045が登場し、MIME(Multipurpose Internet Mail Extensions)を定義しました。MIMEはメール添付ファイルの動作を標準化し、Base64はバイナリコンテンツの主要なエンコーディングでした。今日でも生のメールメッセージにContent-Transfer-Encoding: base64ヘッダーが見られるのはそのためです。

エンコーディングはRFC 4648(2006年)で再び改良され、これが決定的なリファレンスとなりました。このRFCではURL安全な変種(Base64URL)も導入され、パディングに関するエッジケースも明確化されました。MDNのBase64用語集ページは、生のRFCを読まずに仕様を調べたい場合の良い出発点です。

興味深いのは、Base64は7ビットメールという、もはや存在しない制約から生まれたということです。現代のSMTPは8ビットおよびバイナリ転送をサポートしています。それでもBase64は元の目的をはるかに超えて生き続け、Web API、JWT、データURI、そしてその他無数のコンテキストで新たな命を見出しています。問題をあまりにもうまく解決したために、人々がそれに解決させる新しい問題を見つけ続けている技術の一つです。

自分で試してみよう

Base64を実際に体験してみませんか?任意のテキストやファイルをBase64 Encoderに貼り付ければ、エンコードされた出力が即座に表示されます。デコードが必要ですか?Base64 Decoderがそれを処理します。そしてBase64エンコードされた画像を扱っている場合は、Base64 to Imageツールでブラウザ上で直接プレビューできます。