Some bits in the final character carry no data

RFC 4648 §3.5 requires conforming encoders to set pad bits to zero. With one input byte, only the top two bits of the second Base64 character carry data; the other four are pad bits. The character g has index 32, or 100000; h has index 33, or 100001. The byte for f is 01100110, and the data bits match in both representations. Zg== is canonical output; Zh== has nonzero pad bits. Do not confuse trailing = characters with the bits that are discarded.

Zg== and Zh== have different final four bits but restore the same byte for f
An original bit diagram. Blue marks data; gray marks discarded pad bits. The byte 01100110 represents the public sample f.

RFC 4648 §3.5, §4: Canonical Encoding / Base64

Check decoding and re-encoding with the actual tools

WHATWG Infra forgiving Base64 decoding discards four trailing bits from a remaining 12-bit group, or two from an 18-bit group. HTML atob uses that algorithm. Moyoutil restores bytes with atob and interprets them as UTF-8 text. In our executed examples, Zg== and Zh== both returned f; Zm8= and Zm9= both returned fo. Re-encoding produced Zg== and Zm8=, respectively. The code below re-encodes bytes directly rather than converting arbitrary binary data to UTF-8 text.

for (const input of ['Zg==', 'Zh==', 'Zm8=', 'Zm9=']) {
  const bytes = atob(input);
  const canonical = btoa(bytes);
  console.log(input, bytes, canonical, input === canonical);
}
// Zg== f Zg== true
// Zh== f Zg== false
// Zm8= fo Zm8= true
// Zm9= fo Zm8= false

Equal bytes and equal original strings are different conditions. Decide which equality your cache key or comparison needs. If the receiving protocol requires canonical input, validate its rules. We do not recommend changing signed strings before verification. This re-encoding comparison is limited to padded standard Base64 and is not a validator for every protocol.

WHATWG Infra: Forgiving base64 decode

WHATWG HTML: atob

Frequently Asked Questions

Does every decoder accept Zh==?

No. RFC 4648 allows rejection of nonzero pad bits, and a referring specification can define the behavior. These results describe HTML atob and the current Moyoutil implementation.