Base64 explained: why line breaks and padding break your strings

What Base64 actually does, why those "=" signs matter, why MIME line breaks corrupt your data, and the unicode trap everyone hits.

Base64 looks like it should be simple — encode text, decode it back, done. Then you paste a Base64 string from an email attachment and the decoder throws an error, or your emoji comes back as garbage. Both failures have the same root cause: people treat Base64 as opaque magic when it’s actually a very specific encoding with rules you have to respect.

What Base64 actually is

Base64 encodes binary data into a safe ASCII string using 64 characters: A–Z, a–z, 0–9, +, and / (plus = for padding).

Under the hood it takes input bytes and re-splits them into 6-bit groups (2⁶ = 64, hence the name):

  • 3 input bytes (24 bits) → 4 Base64 characters (4 × 6 = 24 bits).

The mismatch is where all the headaches come from. Your input isn’t always a clean multiple of 3 bytes, so when only 1 or 2 bytes remain, the encoder pads the output with = signs:

"hello"   → aGVsbG8=
"hi"      → aGk=

Those trailing = tell the decoder how many bytes to drop. They’re not optional decoration — a correct decoder treats them as part of the encoding. If you strip them or add extra ones, decoding may fail or silently corrupt the tail of your data.

Why line breaks break things

Here’s the classic real-world bug. Old email systems (MIME) wrap Base64 at 76 characters per line. So an attachment encoded in an email looks like:

aGVsbG8gd29ybGQgdGhpcyBpcyBhIGxvbmdlciB0ZXN0IG9mIGJhc2U2NCBlbmNvZGluZyB0aGF0
IGdvaW5nIHRvIG5lZWQgbW9yZSB0aGFuIG9uZSBsaW5l...

That embedded newline is part of the MIME transport format, not part of the Base64 data. Many decoders are strict and reject it; others accept it. When a decoder says “invalid character” and you’re staring at what looks like valid Base64, line breaks are the first suspect. The fix: strip whitespace/newlines before decoding, or use a tool that handles it automatically.

There’s a second, subtler version of the same problem: URL-safe Base64. Because + and / aren’t URL-safe, URLs use - and _ instead, and often drop padding. A JWT’s base64url payload looks like Base64 but is a distinct variant — decode it with a standard Base64 decoder and it can fail on the -/_ characters.

The unicode trap

The biggest surprise for most people: Base64 encodes bytes, not characters.

When you base64-encode the string "café", the encoder must first decide what bytes "café" is. If the input is UTF-8, é is 2 bytes. If it’s Latin-1, it’s 1 byte. The same string produces different Base64 for each encoding. The universal fix: always encode your input as UTF-8 first, and decode to UTF-8 after. If you skip that step, ASCII text round-trips fine (that’s why it doesn’t bite you immediately), but any non-ASCII character — accented letters, emoji, CJK — comes back garbled.

How to avoid the three traps

  • Padding: don’t strip = signs from standard Base64 unless the spec says it’s unpadded.
  • Line breaks: if a string has embedded newlines, remove them before decoding.
  • Unicode: encode/decode with an explicit UTF-8 charset, always.

A Base64 encoder/decoder that handles all three automatically — padding-tolerant, line-break tolerant, and always UTF-8 — turns this into a two-second operation instead of a debugging session. And since it runs in the browser, the string you paste never leaves your machine.