Learn what Base64 encoding is, how it works, and when to use it. Includes practical examples and a free online Base64 encoder/decoder tool.
The first time I encountered Base64, I thought it was encryption. I was staring at a long string of seemingly random characters in an API response and assumed somebody had encrypted the data. I spent twenty minutes looking for the decryption key before a colleague pointed out it was just Base64 — an encoding scheme, not a cipher. Anyone can decode it. There is no key.
That misunderstanding is surprisingly common. Base64 shows up everywhere in software development — in emails, in API payloads, in data URIs, inside JWTs — and developers interact with it constantly without always understanding what it actually does or why it exists.
This post breaks down Base64 encoding from first principles. What it is, how the algorithm works, where you should use it, and where you definitely should not.
Base64 is a binary-to-text encoding scheme. It takes arbitrary binary data — bytes that might include unprintable characters, null bytes, or values that would break text-based protocols — and converts it into a string made entirely of printable ASCII characters.
The name comes from the fact that it uses 64 characters to represent data. That's it. No magic, no compression, no security. Just a way to represent binary data using a limited alphabet of safe, printable characters.
The Base64 alphabet consists of:
A-Z (26 characters)a-z (26 characters)0-9 (10 characters)+ and / (2 characters)= (padding character)That gives you 64 characters for encoding (plus the padding character). Every character in a Base64 string maps to exactly 6 bits of data, because 2^6 = 64.
The encoding algorithm is straightforward once you see it step by step.
Step 1: Convert input to binary. Take your input data and get its binary representation. Each byte is 8 bits.
Step 2: Split into 6-bit groups. Take the stream of bits and divide it into chunks of 6 bits each. Since the original data comes in 8-bit bytes but Base64 uses 6-bit groups, three bytes (24 bits) of input produce exactly four Base64 characters (4 × 6 = 24 bits).
Step 3: Map each 6-bit group to a Base64 character. Use the Base64 index table to convert each 6-bit value (0–63) to its corresponding character.
Step 4: Add padding if necessary. If the input length isn't a multiple of 3 bytes, pad the output with = characters to make the final output length a multiple of 4.
Let's walk through a concrete example. Encoding the string "Hi":
Character: H i
ASCII: 72 105
Binary: 01001000 01101001
Split into 6-bit groups:
010010 000110 1001xx
(The last group only has 4 bits, so pad with two zeros)
010010 000110 100100
Base64 index:
010010 = 18 = S
000110 = 6 = G
100100 = 36 = k
Output: SGk=
(One = padding because input was 2 bytes, not 3)
The padding rules are simple:
===Every 3 bytes of input become 4 characters of output. That means Base64-encoded data is always approximately 33% larger than the original. This is the fundamental tradeoff — you gain compatibility with text-based systems, but you pay for it with increased size.
For a 1 MB image, the Base64 representation will be roughly 1.33 MB. For a 10 KB JSON payload, you'll get about 13.3 KB. In most cases this overhead is acceptable, but it adds up quickly if you're encoding large files or high volumes of data.
The standard Base64 alphabet includes + and /, which have special meanings in URLs. URL-safe Base64 (also called Base64url) replaces these:
+ becomes -/ becomes _= is often omittedYou'll see URL-safe Base64 in JWTs, in query parameters, and anywhere Base64 data needs to travel through URLs without being percent-encoded. If you've ever decoded a JWT and wondered why the segments look almost-but-not-quite like regular Base64, this is why.
This is the original use case. Email was designed for 7-bit ASCII text. Binary attachments — images, PDFs, zip files — can't be transmitted as raw bytes through SMTP. MIME (Multipurpose Internet Mail Extensions) uses Base64 to encode attachments into ASCII text that survives the journey through mail servers.
You can embed images directly in HTML or CSS using data URIs:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />This eliminates an HTTP request for small images. It's common for icons, tiny logos, or placeholder images. For anything larger than a few KB, though, the 33% size increase and the inability to cache the image separately make this a bad tradeoff.
JSON doesn't support binary data natively. When an API needs to transmit a file, an image, or any binary content within a JSON payload, Base64 encoding is the standard approach:
{
"filename": "report.pdf",
"content": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAov...",
"contentType": "application/pdf"
}JWTs consist of three Base64url-encoded segments separated by dots. The header, payload, and signature are each independently Base64url-encoded. This is why you can decode a JWT and read its claims without any key — the payload is encoded, not encrypted.
Sometimes you need to store binary data in a database column that only supports text, or in a configuration file, or in an environment variable. Base64 gives you a safe text representation.
Modern JavaScript provides built-in functions for Base64 encoding and decoding:
// Encoding a string to Base64
const encoded = btoa("Hello, World!");
console.log(encoded); // "SGVsbG8sIFdvcmxkIQ=="
// Decoding Base64 back to a string
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
console.log(decoded); // "Hello, World!"The btoa() and atob() functions work fine for ASCII strings, but they break on Unicode. For Unicode support, you need an extra step:
// Encoding Unicode to Base64
function encodeBase64(str) {
const bytes = new TextEncoder().encode(str);
const binString = Array.from(bytes, (b) => String.fromCodePoint(b)).join("");
return btoa(binString);
}
// Decoding Base64 to Unicode
function decodeBase64(base64) {
const binString = atob(base64);
const bytes = Uint8Array.from(binString, (c) => c.codePointAt(0));
return new TextDecoder().decode(bytes);
}
console.log(encodeBase64("Héllo Wörld")); // "SMOpbGxvIFfDtnJsZA=="
console.log(decodeBase64("SMOpbGxvIFfDtnJsZA==")); // "Héllo Wörld"In Node.js, you can also use the Buffer class:
// Node.js
const encoded = Buffer.from("Hello, World!").toString("base64");
const decoded = Buffer.from(encoded, "base64").toString("utf-8");Python's base64 module handles both standard and URL-safe variants:
import base64
# Encoding
text = "Hello, World!"
encoded = base64.b64encode(text.encode("utf-8"))
print(encoded) # b'SGVsbG8sIFdvcmxkIQ=='
# Decoding
decoded = base64.b64decode(encoded).decode("utf-8")
print(decoded) # Hello, World!
# URL-safe variant
url_safe = base64.urlsafe_b64encode(text.encode("utf-8"))
print(url_safe) # b'SGVsbG8sIFdvcmxkIQ=='
# Encoding binary files
with open("image.png", "rb") as f:
image_base64 = base64.b64encode(f.read()).decode("ascii")Notice that Python's b64encode returns bytes, not a string. You'll often chain .decode("ascii") or .decode("utf-8") to get a string you can embed in JSON or HTML.
This cannot be stressed enough. Base64 provides zero security. Anyone can decode a Base64 string instantly. Never use it to hide passwords, tokens, API keys, or any sensitive data. If you need to protect data, use actual encryption (AES, RSA) or hashing for passwords.
I've seen API keys, database credentials, and even user passwords "secured" with Base64 in production codebases. If you can decode it by pasting it into a free online tool, it's not security.
A surprisingly common bug is encoding data twice. The first pass produces valid Base64 output. The second pass encodes that output again, producing a longer string that looks like Base64 but decodes to gibberish. If your decoded output looks like another Base64 string, try decoding it again.
Some implementations insert newlines every 76 characters (the MIME standard). Others don't. When decoding, most libraries handle both, but when comparing Base64 strings or using them in URLs, whitespace differences can cause subtle bugs.
Base64 encoding and URL encoding solve different problems. URL encoding (percent-encoding) makes individual characters safe for URLs by replacing them with %XX sequences. Base64 converts entire binary sequences into a text-safe alphabet. They're complementary — sometimes you'll even need to URL-encode a Base64 string if it contains +, /, or = characters.
Good use cases:
Bad use cases:
If you want to experiment with Base64 encoding and decoding without writing any code, try the Base64 Encoder/Decoder tool. Paste in any text or Base64 string, and it converts instantly in both directions. It supports both standard and URL-safe Base64, handles Unicode correctly, and runs entirely in your browser — nothing is sent to a server.
For related encoding and decoding needs, you might also find these useful:
Base64 is one of those foundational pieces of software infrastructure that's easy to take for granted. It's not glamorous. It doesn't solve complex algorithmic problems. But it shows up in virtually every web application, every email system, and every API that handles binary data.
The key takeaways: Base64 is encoding, not encryption. It makes binary data safe for text-based systems at the cost of a 33% size increase. Use URL-safe Base64 when the output will appear in URLs. And never, ever use it as a security measure.
Understanding how it works at the bit level — the 6-bit grouping, the padding rules, the character table — makes you better at debugging the inevitable issues that come up when you're dealing with encoded data across different systems, languages, and protocols. The next time you see a string ending in == and wonder what's going on, you'll know exactly what you're looking at.