Back to blog

Article

Base64, Demystified: What It Is, Where It Hides, and Why It Is Not Encryption

4/16/20256 min readby xlocale Team
base64encodingdata-urijwtdeveloper-tools

You have pasted it into a config file. You have squinted at it inside a JWT at 1am. You may have even shipped a bug because you assumed it was doing something it wasn't. Base64 is one of the oldest, most boring, most quietly load-bearing pieces of the web — and misunderstanding it causes real production incidents. Let's fix that.

What Base64 actually is

Base64 is a binary-to-text encoding. Its only job is to take arbitrary bytes and represent them using a small, safe set of printable ASCII characters, so the data survives systems that were built for text and choke on raw binary.

The mechanism is simple. Base64 reads the input three bytes (24 bits) at a time, then slices those 24 bits into four 6-bit groups. Each 6-bit group indexes into a 64-character alphabet:

  • AZ (values 0–25)
  • az (values 26–51)
  • 09 (values 52–61)
  • + and / (values 62 and 63)

That is where the "64" comes from — 2^6 = 64 possible values per output character. When the input length is not a multiple of three, the encoder pads the output with = so the result is always a multiple of four characters. Padding carries no data; it only signals how many trailing bytes were real.

Because every output character is a plain ASCII letter, digit, +, /, or =, the result travels safely through pipes that would mangle raw bytes — HTTP headers, JSON strings, XML, and 7-bit email.

Where it shows up in real work

Once you know the shape, you start seeing Base64 everywhere:

  • Data URIs. <img src="data:image/png;base64,iVBORw0KGgo..."> inlines a whole image into HTML or CSS. No extra network request, at the cost of a larger document.
  • JWT segments. A JSON Web Token is three Base64url-encoded parts joined by dots: header, payload, signature. Decode the first two and you can read the claims (they are not secret — more on that below).
  • Email and MIME. SMTP was designed for 7-bit text. Attachments and non-ASCII bodies get Base64-encoded so binary survives the trip through mail servers untouched.
  • Binary inside JSON/XML. JSON has no native byte type. When an API must ship a file, a thumbnail, or a cryptographic blob inside a JSON field, Base64 is the standard escape hatch.
  • Basic auth headers. Authorization: Basic <base64(user:pass)> is Base64, not a security measure. It is trivially reversible by anyone who sees the header.

A quick word on the URL-safe variant: standard Base64 uses + and /, which have special meaning in URLs and filenames. Base64url swaps them for - and _ and usually drops the = padding. JWTs and OAuth tokens use this variant so the encoded value can sit in a URL or path without extra escaping.

The gotcha that bites everyone: it is not encryption

This is the single most important thing to internalize. Base64 is encoding, not encryption. There is no key, no secret, and no protection. Anyone can decode it in one line:

echo "cGFzc3dvcmQxMjM=" | base64 --decode
# password123

Encoding is a reversible transform for transport. Encryption is a keyed transform for confidentiality. Base64 provides zero confidentiality — it does not even provide obfuscation worth the name. If you Base64 a password, an API key, or a session secret and call it "protected," you have shipped a plaintext secret with an extra step. The bytes are as exposed as if you had written them out directly.

Rule of thumb: reach for Base64 when you need compatibility (get bytes through a text-only channel). Reach for real cryptography when you need secrecy.

The 33% size tax

Base64 is not free. It turns every 3 bytes of input into 4 characters of output, so the encoded form is roughly 133% the size of the original — a ~33% overhead before you count padding and any line breaks the format adds.

| Input size | Base64 output (approx) | |---|---| | 3 bytes | 4 chars | | 1 KB | ~1.33 KB | | 1 MB | ~1.37 MB |

That tax is fine for a 2 KB inline SVG. It is a bad trade for a 4 MB hero image jammed into a data URI, where you inflate the payload, block it from being cached separately, and push it past the point where the browser can stream and decode it lazily. Use Base64 for small assets and glue data, not as a general-purpose file transport. If you are sizing a payload before you commit to inlining, the file size estimator will tell you what the encoded blob will actually weigh.

A worked example

Encoding the ASCII string Hi! walks through the whole algorithm in one shot:

Input bytes:  H         i         !
ASCII:        72        105       33
Binary:       01001000  01101001  00100001
Regroup 6:    010010 000110 100100 100001
Values:       18     6      36     33
Alphabet:     S      G      k      h
Output:       SGkh

Three bytes in, four characters out, no padding needed because 3 is a clean multiple of 3. Round-tripping it on the command line:

# Encode
printf 'Hi!' | base64
# SGkh

# Decode
echo 'SGkh' | base64 --decode
# Hi!

When the input length is not divisible by 3, watch the padding appear:

printf 'Hello' | base64
# SGVsbG8=   (5 bytes -> one '=' pad)

Key takeaways

  • Base64 is a binary-to-text encoding built on a fixed 64-character alphabet (A–Z, a–z, 0–9, +, /), with = as pure padding.
  • It exists for compatibility, not size or security — it lets bytes ride through text-only channels like HTTP headers, JSON, XML, and email.
  • It is not encryption. Anything Base64-encoded is trivially decodable; never treat it as a way to hide secrets.
  • Expect a ~33% size increase. Inline small assets, not large files.
  • The URL-safe (Base64url) variant swaps +// for -/_ and drops padding — that is what JWTs and OAuth tokens use.

Next time you hit a mystery blob in a header, a token, or a config, don't guess at it — decode it and see. Paste text or drop a file into the Base64 tool to encode and decode instantly, toggle URL-safe mode for JWT work, and check the byte size before you inline it.

End of article