DEV Community

withuse
withuse

Posted on Originally published at base64.withuse.io AI-assisted

atob() does not require padding. It rejects half-padding.

A common claim about Base64 in the browser is that the input length must be a multiple of 4. It isn't. atob decodes unpadded input fine. What it rejects is something else.

Here are all five cases, run:

"YQ=="     len % 4 = 0   ->  "a"
"YQ"       len % 4 = 2   ->  "a"        <- no padding, still decodes
"YWJjZGU"  len % 4 = 3   ->  "abcde"    <- decodes
"YWJjZ"    len % 4 = 1   ->  throws
"YQ="      len % 4 = 3   ->  throws     <- half-padded
Enter fullscreen mode Exit fullscreen mode

So the rule has nothing to do with multiples of 4.

  • Remainder 2 or 3 decodes without padding. The leftover bits are enough to reconstruct the bytes.
  • Remainder 1 always fails. Six bits cannot encode a byte, so an encoder can never produce that length in the first place.
  • If you pad, pad fully. "YQ=" is rejected even though its length mod 4 is 3 — the same remainder as "YWJjZGU", which passes.

The actual contract of atob is "pad correctly, or do not pad at all", not "length must be a multiple of 4".

Where this bites

The rule is not the same across languages. Same input, "YQ":

JavaScript   atob("YQ")                             ->  "a"
Python       base64.b64decode("YQ")                 ->  binascii.Error: Incorrect padding
Go           base64.StdEncoding.DecodeString("YQ")  ->  illegal base64 data at input byte 0
Enter fullscreen mode Exit fullscreen mode

JavaScript is the lenient one and Go is the strictest. A value that round-trips fine in the browser and is then handed to a Python or Go service is a common way to get an error that looks like it came from nowhere — the data was never wrong, the two decoders just disagree about what counts as well-formed.

If you generate Base64 in the browser and consume it elsewhere, normalize the padding before it crosses the boundary, not after.

Verified

Node v23.7.0, Python 3, Go 1.22.0, run on 2026-09-14. The longer write-up — including Go's four encodings, and why picking the wrong one can pass every test and still break in production on one payload — is here: https://base64.withuse.io/base64-in-go/

Top comments (0)