Base64 エンコード完全ガイド — 仕組み・使い所・落とし穴

バイナリデータとコードが並ぶコンピュータ画面 Photo: Unsplash

JWT のペイロードをデコードしようとしたら atob がエラーを吐いた、メールに添付した画像が壊れて届いた、HTML に埋め込んだアイコンが文字化けして表示されない——こういうとき、だいたい裏側にいるのが Base64 です。「バイナリを ASCII 文字列で表現するやつ」くらいの理解でもなんとか動くのですが、サイズがどれくらい増えるのか、なぜ URL で +/ が壊れるのか、日本語を btoa に渡すとなぜ例外が飛ぶのか、ちゃんと説明できる人は意外と少ないよね。本記事では Base64 の仕組みをビット単位でほぐしながら、実務で詰まりがちな落とし穴までまとめて整理します。読み終わるころには「なぜ 33% 増えるのか」を堂々と説明できるようになっているはずです。

Base64 はなぜ 64 文字なのか

Base64 の原型は 1987 年の RFC 989「Privacy Enhancement for Internet Electronic Mail(PEM)」で定義されました。当時のメールは 7 ビット ASCII しか安全に通せず、バイナリ添付を壊さず運ぶための「印刷可能文字だけで表現する符号化」が必要だったのです。現在の仕様は 2006 年の RFC 4648 にまとまっており、Base16(Hex)・Base32・Base64 の三種が同じ文書内で定義されています。なぜ 64 かというと、2⁶ = 64 つまり 6 ビットをちょうど 1 文字で表現できるから。64 文字はすべて印刷可能な ASCII のサブセット(英大文字・英小文字・数字・記号 2 種)から選ばれており、テキストとしてほぼどんな経路でも壊れにくい、という実用的な落とし所になっています。

Base64 とは何か

Base64 は「任意のバイナリデータを、印刷可能な ASCII 文字 64 種だけで表現する符号化方式」です。一言でいえば「バイト列をテキストに化けさせる仕組み」。暗号化でも圧縮でもなく、単なる見た目の変換です。入力が同じなら出力も常に同じ、逆変換も鍵なしで誰でもできます。これは欠点ではなく Base64 の設計目的そのもので、テキストしか通らない経路でバイナリを運ぶための「輸送用ラッピング」だと考えるとしっくりきます。

歴史的な出発点はメールです。SMTP(Simple Mail Transfer Protocol)は長らく 7 ビット ASCII しか保証してこなかったため、画像や実行ファイルのように 0x00〜0xFF の全バイトを含むデータをそのまま流すと、途中のサーバーが 8 ビット目を勝手に落としたり、改行コードを書き換えたりしてデータが壊れてしまう危険がありました。Base64 で ASCII の範囲に閉じ込めておけば、どんな経路を通っても中身が保たれる、というわけです。この「壊れにくさ」が評価され、現代でもさまざまな場面で生き残っています。

現在よく見かけるのは次のような場所です。メールの添付ファイル(MIME の Content-Transfer-Encoding: base64)、HTML/CSS に埋め込む小さな画像(data:image/png;base64,...)、JWT(JSON Web Token)のヘッダ・ペイロード・署名、HTTP の Basic 認証ヘッダ(Authorization: Basic dXNlcjpwYXNz)、SSH 公開鍵のエクスポート形式、画像アップロード API で JSON に画像を埋め込むケース、ブラウザと Service Worker の間でバイナリをやり取りする場面などなど。「JSON の中にバイナリを突っ込みたい」「URL に小さなデータを付けたい」といった要望がある限り、Base64 は消えない定番技術です。

一方で誤解も多く、「Base64 でエンコードしたから安全」「Base64 は軽量な圧縮」といった思い込みは実害を生みます。本記事では仕組みを理解したうえで、こうした誤解もまとめて正していきます。

変換の仕組み

Base64 の中心アイデアは非常にシンプルで、「3 バイト(24 ビット)を 4 文字(6 ビット × 4)に詰め替える」だけです。1 バイトは 8 ビット、6 ビットは 2⁶ = 64 通りを表せるので、6 ビットごとに切り出した値を 64 文字のアルファベットに対応させれば、バイナリをテキストに置き換えられます。24 と 8 と 6 の最小公倍数が 24 なので、3 バイトずつ処理するのが一番きれいにビットが割り切れる単位、というわけです。

具体例で見てみましょう。文字列 "Man"(ASCII で 3 バイト)を Base64 にすると "TWFu" になります。バイト列とビット列の対応は次のとおりです。

入力文字ASCII 値2 進表現(8 ビット)
M7701001101
a9701100001
n11001101110

24 ビットを横に並べると 010011010110000101101110。これを左から 6 ビットずつ区切り直すと 4 つのグループができます。

6 ビット列10 進Base64 文字
01001119T
01011022W
0001015F
10111046u

できあがった 4 文字を並べると TWFu。ここで使う 64 文字のアルファベットは RFC 4648 で次のように決まっています。0〜25 が A〜Z、26〜51 が a〜z、52〜61 が 0〜9、62 が +、63 が /。アルファベットの並びは一見任意に見えますが、これも ASCII 順に沿って自然に決まるように設計されています。

インデックス  文字
   0 〜 25    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
  26 〜 51    a b c d e f g h i j k l m n o p q r s t u v w x y z
  52 〜 61    0 1 2 3 4 5 6 7 8 9
     62       +
     63       /

デコードは逆の手順を踏むだけです。4 文字を 6 ビットずつに戻し、24 ビットつなげてから 8 ビット単位で切り直せば元のバイト列に戻ります。鍵も状態も必要ないので、エンコード結果さえあれば誰でも元データを復元できます。ここが次章以降で繰り返し出てくる重要なポイントです。

サイズは 33% 増える — その理由

Base64 でエンコードすると、必ず元データより大きくなります。増加率はだいたい 33%(正確には 4/3 倍)。これはメール添付や Data URI の設計判断に効いてくるので、感覚として掴んでおきたい数値です。

計算式は次のとおりです。入力バイト数を n とすると、出力文字数は次の式で求められます。

output_len = ceil(n / 3) × 4

理由は前章で見たとおりで、Base64 は 3 バイト単位で 4 文字に変換します。3 バイト入力に対して 4 バイト(文字)出力なので、比率はぴったり 4 対 3。割り切れない端数があると、3 バイトに満たない最後のグループもパディングで 4 文字に揃えるため、ごく小さな入力ほど増加率が大きくなります。たとえば 1 バイト入力は 4 文字出力なので増加率は +300%、一方 1 MB の入力なら約 +33% に収束します。具体的な数字を表にまとめました。

入力サイズ出力サイズ増加率
1 バイト4 文字(うちパディング 2)+300%
2 バイト4 文字(うちパディング 1)+100%
3 バイト4 文字+33.3%
100 バイト136 文字+36.0%
1 KB(1024 バイト)1368 文字 ≒ 1.37 KB+33.6%
1 MB約 1.37 MB+33.3%
10 MB約 13.33 MB+33.3%

ここで注意したいのは、MIME メールの仕様では 76 文字ごとに \r\n を挿入することが推奨されている点です。改行もバイト数としてカウントするので、実務上のオーバーヘッドは 33% より少し大きくなります。1 KB の入力なら改行が約 18 ペア入り、出力は 1404 バイト前後。数%の差ですが、数十 MB をまとめて扱う場面では地味に効いてきます。ストレージ料金やネットワーク転送量を見積もるときは「4/3 倍 + 改行」くらいの余裕を見ておくと安心です。

「だいたい 1.33 倍」と覚えておけば、メールで 10 MB の画像を添付するとネット上を流れる実データは 13 MB 強になる、という感覚的な見積もりができるようになります。メールサーバーの添付サイズ制限が 25 MB なら、元データの上限は 18〜19 MB 前後、と逆算できるわけです。

パディング(=)の役割と仕様

Base64 の文字列でときどき末尾に付いている = を見たことがあるはずです。あれは「パディング」と呼ばれる詰め物で、出力長を 4 の倍数に揃えるためのマーカーです。64 文字のアルファベットには含まれておらず、デコード時には無視(あるいは「ここが末尾ですよ」のヒント)として扱われます。

なぜ 4 の倍数に揃えるかというと、Base64 の処理単位が 4 文字 = 3 バイトだからです。入力が 3 の倍数で終わっていれば出力はちょうど 4 文字単位になり、パディングは不要。3 で割って余りが 1 または 2 のときだけ、足りない分を = で補います。

入力バイト数 mod 3出力の末尾パディング数
04 文字すべて有効0 個"Man"TWFu
12 文字 + ==2 個"M"TQ==
23 文字 + =1 個"Ma"TWE=

RFC 4648 では「パディングを付ける」のが基本ルールですが、同じ RFC の中に「アプリケーションによってはパディングを省略してもよい」という逃げ道も明記されています。このため実装によって扱いがまちまちで、= を付けないとデコードが通らないパーサもあれば、付いていなくても通るものもあります。ここが落とし穴のひとつで、後述の URL safe バリアントと絡むと問題が起きやすいポイントです。

厳密な実装を書くなら、エンコード時はパディングを必ず付け、デコード時は付いていなくても受け入れる——ロバストネス原則に沿った作り方が実用上は無難です。ブラウザの atob はパディングに寛容ですが、古い Python や一部の Java ライブラリは厳格に 4 の倍数を要求するため、他人が生成した Base64 を受け取るときは「長さを 4 の倍数に揃える」前処理を挟んでおくと事故が減ります。

// JavaScript でパディングを補う例
function padBase64(s) {
  const rem = s.length % 4;
  return rem === 0 ? s : s + '='.repeat(4 - rem);
}

URL safe バリアント(+/-_

Base64 の 64 文字のうち、+/ の 2 つは URL の中で厄介な存在です。+ はクエリ文字列でスペースを表す予約文字として解釈されることがあり、/ はパスの区切り文字そのもの。Base64 をそのまま URL に埋め込むと、意図せず別の意味に解釈されてデータが壊れる恐れがあります。これを避けるために用意されているのが、RFC 4648 §5 で定義された Base64 URL safe(別名 base64url)です。

違いは次の 2 か所だけ。6 ビット値の変換ロジックも出力長も同じで、単にアルファベットの 2 文字が置き換わるだけです。

インデックス標準 Base64Base64 URL safe
62+-
63/_
パディング= を使用多くの実装で省略

URL safe 版のもう一つの実務上の慣習として、「パディングの = を省略する」というものがあります。= もクエリ文字列では「キーと値の区切り」を意味するため、URL に混ぜると意味が変わってしまうからです。JWT(JSON Web Token)は仕様で明確に「パディングなしの Base64 URL safe」を要求しており、これを知らずに = 付きでデコードさせようとすると署名検証が通らないなどの事故が起きます。

相互変換は単純な文字置換でできます。標準 Base64 → URL safe なら +-/_ に置換し、必要なら = を落とすだけ。逆方向は置換を戻して、長さが 4 の倍数になるまで = を補います。わざわざ専用ライブラリを使わなくても数行で書けるので、覚えておくと便利です。

// 標準 Base64 → URL safe(パディングなし)
const toUrlSafe = (s) => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

// URL safe → 標準 Base64(パディング復元)
const fromUrlSafe = (s) => {
  const padded = s + '='.repeat((4 - s.length % 4) % 4);
  return padded.replace(/-/g, '+').replace(/_/g, '/');
};

Data URI として画像を埋め込む

HTML や CSS で画像ファイルを別のリソースとして参照する代わりに、画像データそのものを文字列として埋め込んでしまう書き方があります。これが Data URI で、Base64 が活躍する代表的な場面です。構文は次のようにシンプルです。

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="small icon">

data: スキームに続けて MIME タイプ(image/png など)、;base64、カンマ、そして Base64 エンコード済みのバイナリを並べるだけ。ブラウザはこれを見て「外部リソースではなく、URI に埋め込まれたデータ」として扱います。画像だけでなく font/woff2application/pdf など任意の MIME タイプを指定できます。

メリットははっきりしていて、HTTP リクエストが 1 本減ります。小さなアイコンをいくつも参照する CSS で、リクエスト数を削って初回表示を速くするテクニックとして広く使われてきました。ビルド時に自動で Data URI に変換する Webpack の url-loader や Vite の ?inline インポートなどがおなじみですね。

ただし現代の Web では、Data URI を乱用するのはだいたい裏目に出ます。理由は主に 3 つ。ひとつは 33% のサイズ増加。画像がそのまま 1.33 倍になって HTML に載るので、HTML 本体のダウンロードが遅くなります。ふたつめは HTTP キャッシュが効かないこと。外部画像なら一度読み込めばブラウザがキャッシュしてくれますが、HTML に埋め込まれた Data URI は HTML と運命を共にするので、毎回送り直しです。みっつめは HTTP/2 と HTTP/3 の普及。多重化されたコネクションではリクエスト数が多少多くても体感は変わらず、むしろ圧縮が効きやすい外部ファイルのほうが有利になる場面が増えました。

Data URI の使いどころは限定的

2026 年現在、Data URI を積極的に使う価値があるのは「1〜2 KB 以下の非常に小さい画像」「メール HTML のように外部リソースを読めない環境」「単体の SVG アイコン」くらいです。数十 KB を超える画像を Data URI にするとキャッシュが効かない分だけむしろ遅くなり、ビルド成果物の diff も爆発するので避けましょう。ロゴやファーストビューのヒーロー画像は普通に <img> タグで外部参照するのが正解です。

SVG をインラインする場合は Base64 を通さずに URL エンコードするだけのほうがサイズが小さくなる、という裏技もあります。data:image/svg+xml;utf8,%3Csvg... の形で書くと、Base64 による 33% 増を回避できて CSS との相性もよくなります。小技ですが知っておくと得です。

メール(MIME)での使われ方

Base64 がもっとも歴史的に活躍してきたのがメールです。前述のとおり SMTP は長く 7 ビット ASCII しか保証しない設計で、画像や動画、ZIP ファイルのようなバイナリを添付するには「全バイトを 0x20〜0x7E の印刷可能 ASCII に収める」必要がありました。そこで登場したのが 1996 年の MIME(Multipurpose Internet Mail Extensions、RFC 2045〜2049)で、Base64 はその中心的なエンコード方式として採用されます。

メールの添付ファイルを生のソースで見ると、次のようなヘッダとボディに出会います。

Content-Type: image/png; name="logo.png"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="logo.png"

iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

Content-Transfer-Encoding: base64 を見たメールクライアントが、それ以降のボディを Base64 としてデコードする、という仕組みです。MIME 仕様では「1 行の長さは 76 文字以下」と推奨されており、多くのメールクライアントは律儀に \r\n で折り返します。これは昔のメールサーバーやリレーが長すぎる行を勝手に切り詰める挙動への配慮ですが、現代では問題になることが少なく、むしろ折り返しを考慮しない Base64 パーサにデータを渡すと「余計な改行が混じっている」と怒られる、という逆の問題が起きます。

現在でも S/MIME 署名付きメールや暗号化メール、マルチパート HTML メールに埋め込む画像(cid: 参照の添付)など、Base64 はメール周辺で現役です。Postfix や Exim のログでメール本文が aW1hZ2UvcG5n... のように見えるのは、このためです。8 ビットクリーンな SMTP(8BITMIME 拡張)が普及した今でも、互換性のために Base64 を使い続けるのが現場のデファクトです。

よくある落とし穴

仕組みが単純なぶん、Base64 は実装の細部で意外と事故が起きます。ハマりやすいポイントをまとめて紹介しておきます。

1. 二重エンコード(ダブルエンコード)

既に Base64 になっている文字列にもう一度 Base64 をかけてしまう、古典的なバグです。URL を作るときや JSON にバイナリを詰めるときに起きやすく、「なぜか文字数が増えているけどデコードはエラーにならない」という厄介な症状になります。ログに dVZXWmY=(これは uVWZf をさらに Base64 した結果)のような「短いのにやけに英数字ばかり」な文字列があったら、二重エンコードを疑ってください。対策は、データの型を明確に分けること。アプリ内部では常にバイト列を持ち、シリアライズする瞬間だけ 1 回だけ Base64 にする、というルールを徹底します。

2. 改行の扱い

前述のとおり MIME は 76 文字ごとに \r\n を挿入します。しかし JWT や Data URI では改行は不要——というより、含まれていると壊れます。標準ライブラリの挙動もバラバラで、Python の base64.encodebytes() は改行を入れ、base64.b64encode() は入れません。Node.js の Buffer.toString('base64') は改行なし、OpenSSL の openssl base64 はデフォルトで改行入りです。デコード側が改行に寛容かどうかも実装によって違うので、エンコード時もデコード時も「改行を含むかどうか」は必ず意識しましょう。迷ったら s.replace(/\r?\n/g, '') で全部消してから処理するのが安全です。

3. 日本語を直接 btoa に渡す

ブラウザの btoa() は名前こそ「Binary to ASCII」ですが、入力として受け取れるのは Latin-1 範囲(0x00〜0xFF)の文字列だけです。日本語を渡すと次のようなエラーが飛びます。

btoa('あ')
// ❌ InvalidCharacterError: The string to be encoded contains
//    characters outside of the Latin1 range.

これは btoa の古い設計が今に引きずられているためで、仕様上「各文字を 1 バイトとみなす」挙動になっています。解決策は、いったん UTF-8 バイト列に変換してから btoa に渡すこと。TextEncoder を使えば次のように書けます。

// UTF-8 → Base64(安全版)
const toBase64Utf8 = (str) => {
  const bytes = new TextEncoder().encode(str);
  let binary = '';
  for (const b of bytes) binary += String.fromCharCode(b);
  return btoa(binary);
};

toBase64Utf8('あ'); // "44GC"

モダンブラウザなら Uint8Array.prototype.toBase64()(2024 年から順次実装)が使える環境もあります。対応状況を確認してからで十分ですが、将来的にはこれが主流になるはずです。

4. URL safe と標準を混ぜる

外部 API から受け取った Base64 文字列を手元のライブラリでデコードしようとしたら失敗する、というときは URL safe と標準を取り違えているケースが多いです。JWT や OAuth 2.0 の PKCE は URL safe、メールや一般的な API は標準、と使い分けがあるので「相手がどちらを返すか」をドキュメントで確認する癖をつけましょう。見分け方は、-_ が含まれていれば URL safe の可能性が高い、+/ が含まれていれば標準、です。

5. 文字列長が 4 の倍数でない

パディングを省略した Base64 を受け取って、古い Java の Base64.getDecoder() に渡すと IllegalArgumentException が飛びます。getDecoder() は厳格にパディングを要求するのに対し、getMimeDecoder() は空白と改行を許容、getUrlDecoder() は URL safe 用、と 3 種類あります。Java を使うときは「どのデコーダを呼ぶか」を意識してください。

Base64 は暗号化ではない

これは本記事でいちばん大事な章です。Base64 は暗号ではありません。鍵もパスワードも使っておらず、アルゴリズムは公開されており、世界中の誰でも秒でデコードできます。それなのに「パスワードを Base64 で保存しているから安全です」「API キーを Base64 に包んでいるので漏れても大丈夫」といった誤用が今もあちこちで見つかります。

Base64 を暗号代わりに使ってはいけない

Base64 は「読めなくする」ための仕組みではなく、「壊れないように運ぶ」ための仕組みです。パスワード・クレジットカード番号・個人情報・API シークレットなど、第三者に知られてはいけないデータを Base64 で保護するのは ゼロ効果です。必要なのは AES-GCM などの認証付き暗号か、パスワードなら bcrypt / scrypt / argon2 でのハッシュ化。Base64 は「何を選ぶか」の候補にすら入りません。

有名な誤用例を挙げておきます。HTTP の Basic 認証Authorization: Basic base64(user:password) という形式ですが、これは「伝送経路の統一フォーマットとして Base64 を使っている」だけで、保護は HTTPS に丸投げです。HTTPS なしで Basic 認証を使うと、パケットを拾った人が Base64 をデコードするだけでパスワードが丸見えになります。同じく JWT のペイロードも Base64 URL safe でエンコードされているだけで、中身は読めます。JWT の信頼性は署名部分(HMAC や RSA)が担保しているのであり、Base64 部分はただの入れ物です。

「なんとなく読みづらいからとりあえず Base64 しておこう」という発想は、セキュリティ的には「ベッドの下に鍵を置いた」くらいの意味しかありません。難読化と暗号化は違う、という原則は繰り返し確認する価値があります。運ぶためのエンコードと、守るための暗号は、別の道具を使いましょう。

ベンリーの Base64 ツール使い方

ここまでの知識を実際に手を動かして確かめたいときは、ベンリーの Base64 エンコード / デコードツール が使えます。テキストでもファイルでも入力可能で、処理はすべてブラウザ内で完結します。入力データがサーバーに送信されることはないので、機密情報を貼っても安全です。

主な機能は次のとおりです。

  • テキスト ↔ Base64: 左右のテキストエリアにそれぞれ入力するだけで、その場でエンコード・デコードが動きます。UTF-8 の日本語もそのまま扱えます(内部で TextEncoder を通しています)。
  • ファイル → Base64: 画像や PDF などの任意のファイルをドラッグ & ドロップすると、Data URI 形式の文字列が得られます。小さな画像をそのまま HTML や CSS に貼り付けたいときに便利です。
  • Base64 → ファイル: 逆方向の変換も可能です。生成されたバイナリはダウンロードボタンからファイルとして保存できます。
  • URL safe 切り替え: トグルひとつで標準 Base64 と URL safe(パディングなし)を切り替えられます。JWT のペイロードを手元で確認したいときに役立ちます。
  • サイズ表示: 入力と出力のバイト数・文字数がリアルタイムで表示されるので、「33% 増える」を目で確認できます。

ブラウザで動くので実用上の上限はマシンのメモリ次第ですが、100 MB クラスのファイルでも問題なく処理できます。コマンドラインツールをインストールせずにサクッと変換したいとき、あるいは本記事で紹介した挙動を試したいときに活用してください。

コピペで動く主要言語のサンプル

手元のプログラムで Base64 を扱いたいときの最短コードをまとめておきます。仕様は同じでも API の形は言語ごとにクセがあるので、引き出しとして覚えておくと便利です。

# Python(標準 Base64)
import base64
base64.b64encode(b'hello').decode()         # → 'aGVsbG8='
base64.b64decode('aGVsbG8=')                # → b'hello'

# Python(URL safe、パディングあり)
base64.urlsafe_b64encode(b'hello').decode() # → 'aGVsbG8='
base64.urlsafe_b64decode('aGVsbG8=')        # → b'hello'

# Node.js(Buffer)
Buffer.from('hello').toString('base64');    // → 'aGVsbG8='
Buffer.from('aGVsbG8=', 'base64').toString();// → 'hello'

# シェル(coreutils の base64 コマンド)
echo -n hello | base64                      # → aGVsbG8=
echo aGVsbG8= | base64 -d                   # → hello

シェルの echo は末尾改行を勝手に付けてしまうので、正確に "hello"(5 バイト)をエンコードしたいときは -n を忘れずに。付け忘れると 1 バイト増えて aGVsbG8K になります(末尾が 8K)。小さなデータで結果が微妙に違うときはまずこれを疑いましょう。

よくある質問

なぜ画像を Base64 で埋め込むとサイズが増えるの?

Base64 は 3 バイトを 4 文字に置き換える符号化なので、原理的に出力は元の約 4/3 倍(+33%)になります。さらに HTML/CSS 内に埋め込むと、HTTP キャッシュが効かずに毎回 HTML と一緒に送り直される分の帯域も余分にかかります。画像そのものの圧縮率は変わらないまま、テキスト化するぶんだけ純粋に膨らむ、と考えてください。小さなアイコンなら誤差の範囲ですが、数百 KB を超える画像を Data URI にするのは避けるのが無難です。

btoa に日本語を渡すとエラーになるのはなぜ?

ブラウザの btoa() は入力を「Latin-1 範囲(0x00〜0xFF)の文字列」として解釈する古い仕様のためです。日本語のようなマルチバイト文字はこの範囲を超えるので、そのまま渡すと InvalidCharacterError が投げられます。解決策は先に UTF-8 バイト列へ変換してから btoa に渡すこと。new TextEncoder().encode(str) でバイト列を作り、各バイトを String.fromCharCode で Latin-1 文字列に変換してから btoa するのが定番です。新しい環境では Uint8Array.prototype.toBase64() が使えるようになってきています。

URL safe Base64 と通常 Base64 は相互変換できる?

はい、単純な文字置換で変換できます。標準 → URL safe は +-/_ に置き換え、必要に応じて末尾のパディング = を削除するだけ。逆方向は置換を戻して、長さが 4 の倍数になるまで = を補います。ビットの並び方も 64 文字アルファベットの順序も同じなので、情報量が変わったり別データになったりする心配はありません。JWT のペイロードを普通の Base64 デコーダで読みたいときなどに、この変換が役立ちます。

Base64 は復号できるから暗号化の意味ないのでは?

そのとおりで、Base64 は暗号化ではありません。鍵を使わず、アルゴリズムは公開されており、誰でも 1 秒でデコードできます。Base64 の目的は「テキストしか通らない経路でバイナリを壊さず運ぶこと」であって、秘密を守ることではありません。パスワード・API キー・個人情報のような「守るべき情報」を Base64 だけで扱うのは危険です。認証付き暗号(AES-GCM)やパスワードハッシュ(bcrypt / argon2)など、本来の暗号・ハッシュ手法を併用してください。Base64 は「運ぶ道具」、暗号は「守る道具」、と覚えておくと混同しません。

パディング = はなくても復号できる?

実装によります。RFC 4648 の本則ではパディングを付けることになっていますが、同じ RFC の中に「アプリケーションが両者の合意のもとで省略してよい」との規定があり、多くのデコーダは末尾の = がなくても問題なくデコードできます。一方で古い Java の Base64.getDecoder() や厳格な C 系ライブラリはパディングを必須扱いにしており、ないとエラーになります。安全策としては、受け取った Base64 を「長さが 4 の倍数になるまで = で補う」前処理を入れておくと、どのデコーダでも安定して動きます。

Base64 と Base32・Base16 は何が違うの?

どれも RFC 4648 で定義された「バイナリをテキストに符号化する」仲間で、使うアルファベットの大きさが違います。Base16(Hex)は 16 文字(0-9A-F)で 4 ビットずつ、Base32 は 32 文字で 5 ビットずつ、Base64 は 64 文字で 6 ビットずつを 1 文字に割り当てます。効率は Base64 が最良(+33%)ですが、大文字小文字を区別しないテキスト経路(電話の口頭伝達、QR コード)では Base32 が向いています。デバッグで人間が読みたいときは Base16 が読みやすい、という住み分けです。

Base64 をサクッと試したいなら

ベンリーの Base64 ツールは、テキストもファイルも貼り付けるだけでその場でエンコード / デコードできます。URL safe への切り替え、サイズ表示、日本語対応まで揃っています。

Base64 ツールを開く →