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:
- Uppercase letters
A–Z(26) - Lowercase letters
a–z(26) - Digits
0–9(10) - Plus
+and slash/(2)
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:
- 1 byte input → 2 chars +
==(e.g.,"A"→"QQ==") - 2 byte input → 3 chars +
=(e.g.,"AB"→"QUI=") - 3 byte input → 4 chars, no padding (e.g.,
"ABC"→"QUJD")
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:
+becomes-/becomes_- Padding
=is often dropped (since URLs already restrict=usage)
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.