[对比] 10 min read

[对比] Base64 vs Base64URL vs Base32

三种编码,出自同一份 RFC。本文讲清它们实际的区别——字母表、开销、可读性、大小写敏感性——并告诉你在 URL、文件名、邮件、二维码和口头播报的场景下应该选哪一个。

April 2026 | comparison

// 速查对比表

                 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 年的单一文档,统一了"base-N 二进制到文本编码"家族。它们共享同一条底层算法:把二进制输入按 log₂(字母表大小) 的宽度重新分组,把每一组映射到一个字符,再按输出字的边界做填充。

• Base64:6-bit 一组 → 每 3 字节对应 4 个字符(4/3 比例,膨胀 ~33%)
• Base32:5-bit 一组 → 每 5 字节对应 8 个字符(8/5 比例,膨胀 ~60%)
• Base16(十六进制):4-bit 一组 → 每 1 字节对应 2 个字符(2/1 比例,膨胀 100%)

三者都是可逆的、确定性的,并且保留输入的每一个字节。它们是编码,不是压缩,也不是加密。任何拿到字符串的人都能解码回原数据。

// 什么时候用 BASE64(标准版,§4)

标准 Base64 是任何非 URL 文本通道的默认选项。MIME 邮件正文、PEM 包裹的证书、HTTP Basic Auth、S/MIME、XML-DSIG、data URI,以及大多数把二进制塞进文本的文件格式,用的都是它。字符 + / = 在带引号的属性值、带换行的邮件头、XML/JSON 字符串里都安全。

当下游消费方是一个能接受任意可打印 ASCII 的文本协议时,请用标准 Base64——特别是 RFC 级规范明确点名要求的场景(MIME:RFC 2045;PEM:RFC 7468;HTTP Basic Auth:RFC 7617;data URI:RFC 2397)。

  • > 邮件附件(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 记录——允许连字符,不允许斜线
  • > 内容寻址存储的键
  • > 由随机字节生成的短链标识符

// 什么时候用 BASE32(§6)

Base32 只用 32 个字符:大写 A–Z 和数字 2–7。体积膨胀约 60%——比 Base64 糟得多——但你换来了三个 Base64 无法提供的非常独特的性质。

大小写不敏感。字母表只有大写。读或敲这个字符串的人可以完全不在意大小写;JBSWY3DPjbswy3dp 解码结果完全相同。

去歧义字符。数字 0、1、8、9 被排除掉,因为它们在常见字体里长得像 O、I、B、g。只用 2–7。这让从纸面或手机屏幕抄录字符串时可靠得多。

二维码 alphanumeric 模式兼容。二维码有一种特殊的"alphanumeric"模式,用 ASCII 的一个子集按 5.5 bits/char 打包。Base32 完全落在这个子集里(外加填充),所以用 Base32 编码的载荷生成的二维码比 Base64 明显要小。

  • > TOTP / HOTP 种子密钥——Google Authenticator、1Password、Authy 都用 Base32
  • > Tor .onion v3 地址——ed25519 密钥的 56 字符 Base32 编码
  • > 以磁力链接形式分享的 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,一,大 D,二,小 e,三,大 F,斜线,加号,小 g,等号。"光是大小写就极容易抄错。

JBSWY3DPEHPK3PXP——Base32。"J-B-S-W-Y,三,D-P-E-H-P-K,三,P-X-P。"大小写无所谓,没有 0/O1/l 的歧义,你可以在电话里高置信度地读出来。

这恰恰是 TOTP 用 Base32 的原因:有人得在二维码无法扫描时把种子从备用页面敲进验证器 app。Base64 的大小写敏感性会产生没完没了的客服工单。

// 二维码体积对比

二维码有一个特殊的"alphanumeric 模式",用一个 45 字符子集——0–9、A–Z(仅大写)、空格以及 $ % * + - . / :——以 5.5 bits/char 打包。任何超出这个子集的字符都会强制二维码进入"byte 模式",每字符 8 bit,生成的二维码明显变大。

Base32 完全落在 alphanumeric 子集里。Base64 则不然——小写字母以及 +/= 都会强制进入 byte 模式(严格说 +/ 在 alphanumeric 集里,但任何小写字母都会触发 byte 模式)。这意味着同一个载荷编码成 Base32 能装进更小的二维码——经常就是 21×21 的清爽二维码与 33×33 的拥挤二维码的差别。

// 解码时的坑

  • > 大小写敏感性——Base64 解码器会拒绝大小写不对的输入(SGVsbG8=sgvSbg8=)。Base32 解码器通常会先归一为大写再查表,所以各种大小写都能接受。
  • > 填充——标准 Base64 要求 = 填充;JWT/base64url 禁止它;Base32 把它作为可选(RFC 4648 §6)。请务必确认你用的解码器期望哪种。
  • > 空白字符——MIME Base64 会在第 76 列用 CR-LF 换行。不少解码器容忍空白,也有一些不容忍。在喂给 atob() 或其等价函数之前先剥掉。
  • > 字母表撞车——把 Base64 喂给 Base32 解码器(反之亦然),起初看上去好像能工作——ABC 在两者里都合法——直到碰上 += 才静默失败。
  • > Crockford Base32 是一种非 RFC 的变体,被 Stripe ID 和一些区块链系统使用。它的字母表不同(排除了 I、L、O、U),并支持校验位。不要把它与 RFC 4648 Base32 混淆。
  • > Base32 扩展 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 比 hex 小 50%,Base32 比 hex 小 25%。这就是为什么 Base64 在邮件和网页内嵌两类场景里胜出,而 hex 留在了调试与哈希展示场景。

// BASE58 / BASE62 / BASE85 又怎么样?

也存在一些非 RFC 的 base-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-safe 切换开关
Base64URL 编码器——URL-safe,并剥除填充
Base32 编码器——RFC 4648 §6 字母表
Base16(十六进制)编码器——用于对照
Base58 编码器——比特币风格、对人友好的字母表

延伸阅读:
Base64 是什么?它是如何工作的?
URL-safe 与标准 Base64——完整故事
用 Base64 处理 UTF-8 与 Unicode