[比較] Base64 vs Base64URL vs Base32
三種編碼、同一份 RFC。這裡說明它們真正的差異 —— 字母表、開銷、可讀性、大小寫敏感性 —— 以及如何為 URL、檔名、電子郵件、QR code 與「口說出來的」代碼挑選正確變體。
// 快速比較表
Base64 Base64URL Base32
(RFC 4648 §4) (RFC 4648 §5) (RFC 4648 §6)
────────────────────────────────────────────────────────────────
Alphabet size 64 64 32
Bits per char 6 6 5
Characters used A-Z a-z 0-9 A-Z a-z 0-9 A-Z 2-7
+ / - _ (+ = for pad)
Padding = (required) = (optional) = (optional)
Case-sensitive Yes Yes No (A = a)
Size overhead ~33% ~33% ~60%
URL-safe No Yes Yes
Filename-safe No Yes Yes
DNS-safe No Yes Yes
Human-readable Medium Medium High
Human-spoken Painful Painful Doable
QR-code friendly OK OK Excellent
(alphanumeric) (alphanumeric) (alphanumeric mode)
// 它們的共同點
三者皆在 RFC 4648 中定義 —— 這份 2006 年的單一文件統整了「以 N 為底把二進位轉為文字」的家族編碼。它們共用相同的底層演算法:取二進位輸入,把位元重新分組成 log₂(字母表大小) 的區塊,把每個區塊對應到一個字元,並在輸出字邊界上補齊填充。
• Base64:6 位元區塊 → 每 3 個位元組對應 4 個字元(4/3 比例,大約 33% 大)
• Base32:5 位元區塊 → 每 5 個位元組對應 8 個字元(8/5 比例,大約 60% 大)
• Base16(十六進位):4 位元區塊 → 每 1 個位元組對應 2 個字元(2/1 比例,100% 大)
三者皆可逆、具決定性,並保留輸入的每一個位元組。它們是編碼,不是壓縮,也不是加密。任何拿到字串的人都能解碼回來。
// 何時該選標準 BASE64 (§4)
標準 Base64 是非 URL 文字通道的預設選擇。MIME 郵件內容、PEM 包裹的憑證、HTTP Basic Auth、S/MIME、XML-DSIG、data URI,以及大多數「文字中嵌二進位」的檔案格式容器都使用它。字元 + / = 在引號屬性值中、郵件標頭(折行後)中,以及 XML/JSON 字串中都是安全的。
當下游消費者是接受任意可列印 ASCII 的文字協定時——尤其是 RFC 規格層級明確指定時(MIME:RFC 2045;PEM:RFC 7468;HTTP Basic Auth:RFC 7617;data URI:RFC 2397)——請使用標準 Base64。
- > 電子郵件附件(MIME base64 傳輸編碼)
- > PEM 憑證、金鑰、CRL(-----BEGIN ... -----)
- > HTTP Basic Authentication 標頭值
- > Data URI:data:image/png;base64,...
- > S/MIME 與 XML-ENC 酬載
- > 傳統的 SOAP / XML-DSIG 簽章
- > JSON 文件中任何之後不會放進 URL 的二進位欄位
// 何時該選 BASE64URL (§5)
只要你的 Base64 輸出碰到 URL、cookie 值、檔名、DNS 標籤,或任何 + / = 會需要百分比編碼的地方,就改用 Base64URL。它用相同的 64 字元字母表,只作兩處替換(+ → -、/ → _),並依慣例省略填充。
- > JWT 權杖 —— 標頭、酬載與簽章都使用 base64url(RFC 7515)
- > OAuth 2.0 PKCE 的 code_challenge(RFC 7636)
- > OpenID Connect 的 state 與 nonce 參數
- > 魔法連結、邀請權杖、密碼重設權杖
- > 可能被複製進 URL 的 cookie 值
- > 從雜湊值產生的檔名 —— 避免檔名中的 /
- > DNS 標籤、TXT 紀錄 —— 允許連字號,不允許斜線
- > 以內容定址的儲存金鑰
- > 由隨機位元組產生的短 URL 識別碼
// 何時該選 BASE32 (§6)
Base32 只使用 32 個字元:大寫 A–Z 與數字 2–7。體積開銷約 60% —— 比 Base64 差得多 —— 但你可以換到三項 Base64 無法匹敵的特定特性。
大小寫不敏感。字母表只有大寫。讀字串或打字的人可以不管大小寫;JBSWY3DP 與 jbswy3dp 解碼結果相同。
消除模糊字元。數字 0、1、8、9 被排除,因為它們在常見字型中看起來像 O、I、B 與 g。只使用 2–7。這讓人類從紙本或手機螢幕上抄寫時可靠得多。
QR code 字母數字模式相容。QR code 有一種特殊的「字母數字模式」,以 5.5 位元 / 字元編碼,使用 ASCII 的子集合。Base32 完全落在這個子集合中(加上填充),所以相同酬載以 Base32 編碼後的 QR code 會比 Base64 編碼後小得多。
- > TOTP / HOTP 密鑰種子 —— Google Authenticator、1Password、Authy 全部使用 Base32
- > Tor .onion v3 位址 —— 以 56 字元 Base32 編碼的 ed25519 金鑰
- > 以磁力連結分享的 BitTorrent info-hash
- > 類 geohash 的人類可分享位置代碼
- > 授權金鑰與產品序號
- > 列印出的復原碼(2FA 備用碼、錢包助記詞)
- > 經由語音通道的類 DTMF 口頭傳遞
- > 需要大小寫不敏感儲存的系統(DNS 標籤)
// 體積開銷 —— 具體例子
// Input: a 32-byte SHA-256 hash
// Raw: 0x89abcdef… (32 bytes, binary — can't put in text)
// Hex (Base16): 40b2e2… (64 chars, 100% overhead)
// Base32: 5ENM4H2TQWMZ3O4OQBJAFY5Q (56 chars, 75% overhead)
// Base64: ia+N7/eZtRsPj5TqFoqUlD… (44 chars, 37% overhead)
// Base64URL: ia-N7_eZtRsPj5TqFoqUlD… (43 chars, 34% overhead, no padding)
// Input: a 16-byte UUID
// Hex: e7a6c1d0-4b7d-4c6c-8e2f-9f1a3e4b5c6d (36 chars incl. dashes)
// Base32: 5WTMDUCLPVGGZDRPTF… (26 chars)
// Base64: 56bB0Et9TGyOL58aPktcbQ== (24 chars)
// Base64URL: 56bB0Et9TGyOL58aPktcbQ (22 chars, no padding)
// 人類可讀性測試
這正是 Base32 大放異彩之處。試著大聲念出這些字串:
• a+b/c1D2e3F/+g= —— Base64。「小寫 a、加號、小寫 b、斜線、小寫 c、1、大寫 D、2、小寫 e、3、大寫 F、斜線、加號、小寫 g、等號。」光是大小寫就容易抄錯。
• JBSWY3DPEHPK3PXP —— Base32。「J-B-S-W-Y、3、D-P-E-H-P-K、3、P-X-P。」大小寫無關緊要。沒有 0/O 或 1/l 的模糊。透過電話念出來也能可靠地傳達。
這正是 TOTP 使用 Base32 的原因:總得有人從 QR code 的備援畫面把種子打進自己的驗證器 App。若用 Base64,光是大小寫敏感就會製造無止境的客服工單。
// QR CODE 尺寸比較
QR code 有一種特殊的「字母數字模式」,用 45 個字元的子集合以 5.5 位元 / 字元的密度編碼:0–9、A–Z(只限大寫)、空白,以及 $ % * + - . / :。任何超出此子集的內容都會讓 QR code 切到「位元組模式」,改為 8 位元 / 字元——結果就是 QR code 明顯變大。
Base32 完全落在字母數字子集內。Base64 則否——小寫字母與 +/= 會強迫進入位元組模式(嚴格說 +/ 也在字母數字集合裡,但只要有任何小寫字母就會強迫切換)。這代表同一份酬載以 Base32 編碼時,QR code 能小得多——常常是好讀的 21×21 QR 與擁擠的 33×33 QR 的差別。
// 解碼陷阱
-
>
大小寫敏感 —— Base64 解碼器會拒絕大小寫錯誤的輸入(
SGVsbG8=≠sgvSbg8=)。Base32 解碼器通常會在查表前把輸入正規化成大寫,因此能容忍任何大小寫。 -
>
填充 —— 標準 Base64 必須有
=填充;JWT/base64url 禁止填充;Base32 讓它可選(RFC 4648 §6)。永遠確認你的解碼器期望為何。 -
>
空白 —— MIME Base64 會在 76 欄以 CR-LF 折行。許多解碼器能容忍空白,有些不行。餵給
atob()或同等函式前先去除空白。 -
>
字母表衝突 —— 把 Base64 餵給 Base32 解碼器(或反過來)起初看似能用——
A、B、C在兩者中都有效——直到遇到+或=才無聲失敗。 - > Crockford Base32 是 Stripe ID 與部分區塊鏈系統使用的非 RFC 變體。它使用不同的字母表(排除 I、L、O、U)並支援檢查碼。別和 RFC 4648 Base32 搞混。
- > Base32 Extended Hex(RFC 4648 §7) —— 另一種保留字典序的 Base32 字母表。用於 DNSSEC 的 NSEC3 紀錄。容易與標準 Base32 搞混。
// 決策流程圖
Does the output go into a URL, cookie, filename, DNS, JWT, or OAuth flow?
│
├─ Yes → Does a human need to type or speak the string?
│ │
│ ├─ Yes → Base32 (uppercase, no ambiguous chars)
│ │ e.g., TOTP seeds, recovery codes
│ │
│ └─ No → Base64URL (more compact, URL-safe)
│ e.g., JWT, short tokens, hash-named files
│
└─ No → Does the output go into a QR code that must stay tiny?
│
├─ Yes → Base32 (fits in QR alphanumeric mode)
│
└─ No → Standard Base64
e.g., email MIME, PEM, HTTP Basic, data URI
// 為什麼不用 BASE16(十六進位)?
十六進位(RFC 4648 §8 稱為 Base16)是萬用後備方案。每個 shell 工具、除錯器與協定都懂它。它大小寫不敏感、對人類極易讀、也極易實作。但它會把酬載尺寸加倍(100% 開銷),所以只用在小型、固定長度的識別碼上:SHA-256 雜湊(64 個十六進位字元 = 32 位元組)、UUID(32 個十六進位字元 = 16 位元組)、MAC 位址、除錯器中的記憶體位址。
超過幾十位元組以後,十六進位的傳輸成本會明顯難以忍受。Base64 比十六進位小 50%,Base32 則小 25%。這正是為什麼 Base64 贏得了電子郵件與網頁嵌入的用例,而十六進位則停留在除錯與雜湊顯示情境。
// 那 BASE58 / BASE62 / BASE85 呢?
還有一些非 RFC 的以 N 為底的編碼存在於特定領域:
• Base58 —— 比特幣地址、Flickr 照片 ID。排除四個模糊字元(0、O、I、l)與 +/,以便人類抄寫。約 37% 開銷。
• Base62 —— 短網址、Twitter snowflake ID。只使用英數字(無特殊字元),在 URL 中無需跳脫。約 34% 開銷。
• Base85 / Ascii85 / Z85 —— PostScript、PDF、較舊的 ZeroMQ 訊框。約 25% 開銷(比 Base64 更緊湊)但含有棘手的字元選擇,可能導致 XML/JSON 跳脫地獄。
這些挺有趣,但都不是 RFC 4648 標準。如果你要發佈一個公開協定,Base64 或 Base64URL 幾乎永遠是更安全的預設,因為每種語言的標準函式庫都已原生支援。若你選了 Base58,就得把相依套件推送給每一位消費者。
// 並排試用
• Base64 編碼器 —— 標準字母表,附 URL 安全切換
• Base64URL 編碼器 —— URL 安全、已剝除填充
• Base32 編碼器 —— RFC 4648 §6 字母表
• Base16(十六進位)編碼器 —— 供比較
• Base58 編碼器 —— 比特幣式的人類友善字母表
延伸閱讀:
• 什麼是 Base64?它是如何運作的?
• URL 安全 vs 標準 Base64 —— 完整故事
• Base64 處理 UTF-8 與 Unicode