The Problem Base64 Solves
Computers store everything as bytes, and plenty of those bytes aren't printable text: images, encryption keys, compiled code. But many of our oldest and most important systems (email, URLs, JSON, XML) were built to carry text, and feeding them raw binary breaks things: control characters get interpreted, encodings mangle bytes, protocols choke.
Base64 is the bridge: it re-expresses any bytes using only 64 safe, printable characters (A to Z, a to z, 0 to 9, plus + and /). Any binary data becomes a string that survives every text pipeline ever built, and decodes back to the exact original bytes.
How It Works
The mechanism is elegantly simple:
- Take the input bytes as a bit stream: each byte is 8 bits.
- Regroup the stream into chunks of 6 bits instead of 8.
- Each 6 bit value (0 to 63) maps to one character in the Base64 alphabet.
Worked example with the word Hi:
H i
01001000 01101001 two 8-bit bytes (16 bits)
010010 000110 1001(00) regrouped into 6-bit chunks (padded)
18 6 36
S G k = "SGk"
Base64: SGk= (= pads to a multiple of 4)
Because 3 bytes (24 bits) become exactly 4 characters, Base64 output is always about 33% larger than the input, and lengths not divisible by 3 get = padding at the end. That size tax is the price of universal safety.
Try it live: our free Base64 encoder/decoder converts both directions instantly, entirely in your browser.
A Second Worked Example: A Clean Multiple of Three
The "Hi" example above needed padding because two bytes doesn't divide evenly by three. Here's what happens when the input already lines up, using the three bytes "Cat" (67, 97, 116 in decimal, or 01000011 01100001 01110100 in binary):
C a t
01000011 01100001 01110100 three 8-bit bytes (24 bits)
010000 110110 000101 110100 regrouped into four 6-bit chunks
16 54 5 52
Q 2 F 0 = "Q2F0"
Base64: Q2F0 (no padding needed, 3 bytes -> 4 chars exactly)
Notice there's no trailing = this time. Padding only appears when the input length isn't a clean multiple of 3 bytes, which is why some Base64 strings end in = or == and others don't: it's purely a function of the original byte count, not a stylistic choice.
A Concrete Size Example: Data URIs
A small 1 KB PNG icon embedded directly in CSS as a data URI (background-image: url(data:image/png;base64,...)) saves an extra HTTP request, which matters for tiny repeated icons like a chevron or a spinner. But because of the 33% overhead, that same 1 KB file becomes roughly 1.33 KB of text sitting inline in your HTML or CSS on every single page load, uncached and re-parsed every time. For a handful of small icons the tradeoff favours inlining; for a 200 KB photo it doesn't, since you'd rather serve a cacheable image file once than bloat every page's HTML by 266 KB of Base64 text. The rule of thumb developers use: inline it if it's small and reused inline constantly, link to it as a normal file if it's large or cacheable on its own.
Where You Meet Base64 Every Day
- Email attachments: The original use case. SMTP was designed for 7 bit text, so every attachment you've ever sent traveled as Base64 (via MIME).
- Data URIs: Images embedded directly in HTML or CSS: data:image/png;base64,iVBORw0KG. Handy for small icons, wasteful for big files.
- JWTs (JSON Web Tokens): Those eyJhbGciOi tokens in modern auth are three Base64url segments: header, payload, signature. Note: the payload is encoded, not encrypted: anyone can read it.
- HTTP Basic Authentication: The Authorization: Basic header is just username:password in Base64. Generate and inspect them with our Basic Auth tool, and read why that's only safe over HTTPS in our Basic Auth guide.
- API keys and cryptographic material: Keys, signatures, and certificates travel as Base64 (PEM files are Base64 with header lines).
Say It With Us: Base64 Is Not Encryption
This misconception causes real security holes. Base64 has no key and no secret: it's a reversible transformation anyone can undo instantly, in any browser console, with zero knowledge. It provides exactly zero confidentiality.
- cGFzc3dvcmQxMjM= hides nothing: it's "password123" wearing sunglasses.
- Developers who store "encoded" secrets in config files, localStorage, or client side code have stored plaintext with extra steps.
- Encoding transforms format (for transport). Encryption transforms secrecy (with a key). Hashing creates one-way fingerprints (no way back): that third one is explained in our hashing guide.
Rule of thumb: if you can decode it with a free web tool, so can everyone else. Base64 is an envelope, not a lock.
The Variants Worth Knowing
- Base64url: Standard Base64's + and / clash with URL syntax, so URLs and JWTs use a variant with - and _ instead, usually dropping padding. Same idea, swap two characters.
- Base32: A cousin using 32 characters (A to Z, 2 to 7): more verbose but case-insensitive and unambiguous when humans must read or type it. That's why TOTP 2FA secrets use Base32: the code your authenticator app scans is a Base32 string. Full story in our Base32 explainer, with a Base32 converter to experiment, and our TOTP generator to see those secrets producing live codes.
- Hex (Base16): Two characters per byte, favoured for hashes and debugging: compare with our hex converter.
Quick Reference for Developers
// JavaScript (browser)
btoa("Hello") // "SGVsbG8=" (ASCII/Latin-1 only!)
atob("SGVsbG8=") // "Hello"
// Unicode-safe: encode via TextEncoder first
// PHP
base64_encode("Hello"); // "SGVsbG8="
base64_decode("SGVsbG8=");
// Python
import base64
base64.b64encode(b"Hello") # b'SGVsbG8='
The classic JavaScript gotcha: btoa throws on characters outside Latin-1, so emoji and most non-English text need a TextEncoder step first. Our Base64 tool handles Unicode correctly if you want to compare outputs.
Frequently Asked Questions
Why does Base64 output end with = signs?
Padding. Output comes in blocks of 4 characters representing 3 bytes; when input length isn't a multiple of 3, one or two = fill the last block. Decoders use it to know the true byte length (URL-safe contexts often omit it and infer instead).
Can I tell if a string is Base64 by looking?
The signs: only A-Z, a-z, 0-9, +, / characters, length divisible by 4, optional trailing =. It's a strong hint, not proof: "cage" qualifies and is also just a word. Decode and see if the result is meaningful: takes two seconds in our decoder.
Does Base64 compress data?
The opposite: output is one third larger than input. If size matters, compress first (gzip), then Base64 the compressed bytes when a text format is required.
Why do JWTs use Base64 if anyone can read them?
By design: a JWT's job is integrity (the signature proves it wasn't altered), not secrecy. That's also the developer lesson: never put secrets in a JWT payload, because Base64url is decodable by anyone who holds the token.
Is my 2FA QR code Base64?
Close relative: the secret inside the otpauth:// URI is Base32, chosen because humans sometimes type it manually and Base32 avoids ambiguous characters. Decode any 2FA QR with our QR decoder to see the structure, and generate fresh test secrets with the secret generator.
Can I use Base64 to make a password look encrypted?
You can, and plenty of legacy software still does, but it doesn't accomplish anything security-wise: anyone who finds the encoded string can decode it in the time it takes to open a browser tab and paste it into a decoder, no password, key, or special software required. If a system needs to protect a secret at rest, it needs actual encryption (a key someone must possess) or, for things that only ever need to be checked and never read back, hashing. Base64 belongs in the same conversation as those two only in the sense that people confuse it with them, not because it does what they do.