Base64 Is Not Encryption: How It Works and When to Use It
Base64 turns binary data into printable text, expanding it by a third. Learn how the encoding works, why it offers zero secrecy, and when it is the right choice.
Base64 shows up everywhere — email attachments, data URLs, JWTs, API payloads, Kubernetes secrets. It also shows up in a lot of code that treats it as a way to hide data. It is not. This post covers what Base64 actually does and where it belongs.
The problem it solves
Many systems were built to carry text, not arbitrary bytes. Email (SMTP) was originally 7-bit. JSON strings cannot hold raw binary. URLs have reserved characters. HTTP headers are limited to a printable subset of ASCII.
If you need to move a PNG, a cryptographic key, or any other blob through one of these channels, you need a representation made entirely of "safe" characters. That is what Base64 is: a reversible mapping from bytes to a 64-character alphabet.
How the encoding works
The alphabet is A–Z, a–z, 0–9, +, and / — 64 symbols, so each one carries exactly 6 bits (2⁶ = 64).
Input bytes are 8 bits each. The encoder takes 3 bytes (24 bits) and splits them into 4 groups of 6 bits, then maps each group to a character:
Text: "Hi!"
Bytes: 01001000 01101001 00100001
Regrouped: 010010 000110 100100 100001
Index: 18 6 36 33
Output: S G k h
So "Hi!" becomes SGkh. Decoding runs the same table backwards.
Padding
When the input length is not a multiple of 3, the last group is short. The encoder pads it with zero bits and appends = characters to mark how many bytes were missing:
| Input bytes | Output | Padding |
|---|---|---|
Hi! |
SGkh |
none |
Hi |
SGk= |
one = |
H |
SA== |
two = |
Padding makes the output length always divisible by 4. Some decoders accept missing padding; strict ones do not, which is a frequent source of "invalid input" errors when copying values between systems.
Size cost
Every 3 bytes become 4 characters, so Base64 output is 33% larger than the input (plus up to 2 bytes of padding). Embedding a 300 KB image as a data URL costs 400 KB. This matters for page weight and for anything that gets re-encoded more than once.
Why it is not encryption
Encryption transforms data so that only a holder of a key can recover it. Base64 has no key. The mapping is fixed and public, and every decoder on earth reverses it instantly.
Paste any Base64 string into the Base64 Encoder / Decoder and the original appears. That includes:
- The
Authorization: Basic ...header, which is literallyusername:passwordin Base64. - The payload of a JWT.
data:entries in a Kubernetes Secret manifest.
Base64 provides exactly as much secrecy as writing the value in a different font. If you need confidentiality, encrypt first (AES-GCM, for example) and then Base64 the ciphertext for transport.
Base64 vs Base64URL
Standard Base64 uses + and /, both of which have special meaning in URLs. Base64URL (RFC 4648 §5) swaps them for - and _, and usually omits padding. JWTs use Base64URL, which is why you never see = in a token.
Decoding a Base64URL string with a plain Base64 decoder fails on the first - or _. Most libraries expose the two variants separately; pick the one matching your data.
When to use it
Good fits:
- Embedding small binary assets (icons, fonts) inline in CSS or HTML.
- Carrying bytes inside JSON, XML, or YAML fields.
- Putting binary values in HTTP headers or query strings (use Base64URL for the latter).
- Email attachments (MIME).
Poor fits:
- Large files. The 33% overhead and the lack of streaming-friendly boundaries make raw binary or multipart uploads better.
- Hiding anything. Use real encryption.
- Escaping text for HTML or URLs. Those are different problems with their own encodings — see URL Encode / Decode and HTML Entity Encoder.
Summary
- Base64 maps 3 bytes to 4 printable characters using a 64-symbol alphabet.
- Output grows by a third;
=padding rounds it to a multiple of 4. - It is a transport encoding with zero secrecy. Encrypt first if the data is sensitive.
- Use Base64URL when the value will travel in a URL or a JWT.