Base64 Encoding, Explained (And Why It Isn't Encryption)

Base64 is a way of writing arbitrary bytes using only 64 printable ASCII characters. It's everywhere — JWT tokens, email attachments, data URIs, basic auth headers — because it lets binary data ride channels that only accept text. It is not encryption, it does not hide anything, and treating it as security has shipped real bugs. Here's what it actually is.

The problem Base64 solves

Many transports — email bodies, HTTP headers, URLs, JSON strings — are designed for text and choke on certain bytes (newlines, null, control characters, anything outside ASCII). If you need to send a JPEG through email or stash a binary signature in a JSON field, you need a way to write those bytes using only safe characters. Base64 is that way.

The trade-off: every 3 bytes of binary becomes 4 characters of text. Output is roughly 33% larger than the input. That's the price of using a smaller alphabet.

The alphabet

Standard Base64 (RFC 4648) uses 64 characters:

Plus the padding character =, which appears 0, 1, or 2 times at the end of the encoded string to round the length to a multiple of 4.

How the encoding works

Take three input bytes (24 bits) and split them into four 6-bit groups. Each 6-bit group can hold a value 0–63, which maps to one character of the Base64 alphabet.

Worked example: encode the ASCII string "Cat".

'C' = 0x43 = 01000011
't' is wrong wait, "Cat" = 'C', 'a', 't' = 0x43, 0x61, 0x74

Bits:   01000011 01100001 01110100
Group:  010000 110110 000101 110100
Value:  16     54     5      52
Char:   Q      2      F      0  →  "Q2F0"

So "Cat" encodes to "Q2F0". Decoding reverses the process: each character maps back to 6 bits, four characters concatenate to 24 bits, those split into three bytes.

Padding

If your input length isn't a multiple of 3 bytes, the last group of bits is short. Base64 pads the bits with zeros and adds = to mark the truncation:

Some decoders are strict about padding; others accept input without it. RFC 4648 requires it; many real-world parsers don't.

The URL-safe variant

The standard alphabet uses + and /, which have special meaning in URLs and have to be percent-encoded. The URL-safe variant (also RFC 4648) substitutes:

This is the "base64url" variant. JWT tokens use it. So do many web cookies and URL-embedded data. Our Base64 encoder/decoder handles both standard and URL-safe variants — and detects which one you've pasted.

Common uses

Data URIs

Embedding small images directly in HTML or CSS:

<img src="data:image/png;base64,iVBORw0KGgo..." />

Useful for tiny icons or one-off images that don't justify a separate HTTP request. Inflates document size by ~33%, so don't do it for anything large.

HTTP Basic Auth

The Authorization: Basic header carries a Base64 encoding of username:password. The Base64 here is encoding, not protection — anyone who reads the header can decode the credentials in milliseconds. Always use Basic Auth over HTTPS, never on its own.

JWT tokens

Each of the three parts of a JWT is base64url-encoded JSON or binary. Reading a JWT means decoding base64url. See how to decode a JWT for the full structure.

Email attachments

SMTP only allows 7-bit ASCII bodies. Attachments are base64-encoded with line wrapping at 76 characters. This is one of the original use cases (RFC 1521, 1993) and why the encoding exists.

Why Base64 is not encryption

Encoding transforms bytes for transport without protecting them. Anyone, with no key, can decode any Base64 string in milliseconds — your browser does it natively with atob(), every CLI has it, every language ships it as a stdlib function. There is no secret involved.

"We Base64 the password before storing it" is not security. "The API key is Base64 encoded in the request body" does not stop anyone reading the request from getting the key. If you want bytes to be unreadable without a key, you need encryption (AES, ChaCha20) — which produces ciphertext that's then often Base64-encoded for transport. The encoding is the outer layer; the encryption is the inner layer that actually protects.

Common gotchas

Forgetting that Base64 is bytes, not characters

Base64 encodes bytes. To Base64-encode a string, you first need to convert it to bytes — typically UTF-8. "café" in UTF-8 is 5 bytes (the é is two bytes), not 4. btoa() in browsers historically only handled Latin-1; modern code should use btoa(unescape(encodeURIComponent(s))) or the newer TextEncoder API to round-trip Unicode correctly.

Confusing standard with URL-safe

If a base64-encoded value contains - or _, it's URL-safe. If it contains + or /, it's standard. Mixed inputs typically fail. JWT users should always use base64url; data URI users should use standard.

Stripping padding by accident

Some decoders fail on padding-stripped input ("invalid Base64"). If you get a length error, try restoring padding: input + '='.repeat((4 - input.length % 4) % 4).

Encode and decode in the browser

Our Base64 encoder/decoder handles UTF-8 input, both standard and URL-safe variants, and round-trips correctly. Paste, encode, copy. For the JWT-specific use of base64url, our JWT decoder takes a whole token at once and shows you the decoded header and payload.