Base64 Encoding and Decoding: A Complete Guide
If you've ever pasted an image into a CSS file, inspected a JWT token, or sent an email with an attachment, you've used Base64 — whether you knew it or not. It's one of the most common encoding schemes in computing, yet many developers treat it as a black box.
This guide explains what Base64 is, exactly how it works, when to use it, and what it absolutely should not be used for.
What Is Base64?
Base64 is a binary-to-text encoding scheme that represents arbitrary binary data using a set of 64 printable ASCII characters. The alphabet consists of:
- A–Z (26 characters, indices 0–25)
- a–z (26 characters, indices 26–51)
- 0–9 (10 characters, indices 52–61)
- + (index 62)
- / (index 63)
Plus a special padding character = used to fill out the final group when the input length isn't a multiple of three bytes.
The core idea is simple: take every 3 bytes (24 bits) of input, split them into four 6-bit groups, and map each group to one of those 64 characters. Because 26 = 64, each character carries exactly 6 bits of information.
Why Base64 Exists
Many protocols and formats were designed to carry text only — specifically, 7-bit ASCII. When you need to move binary data (images, executables, compressed files) through these text-only channels, you have a problem: arbitrary byte values like 0x00 or 0xFF can corrupt the data or break the protocol.
Base64 solves this by encoding binary data into characters that every text-based system handles safely. Common scenarios include:
- Email (MIME) — SMTP was originally 7-bit ASCII. Email attachments are Base64-encoded so binary files survive the journey through mail servers.
- Data URIs — Embedding images or fonts directly in HTML or CSS using
data:image/png;base64,...avoids extra HTTP requests. - HTTP headers — The
Authorization: Basicheader carries credentials as a Base64-encoded string because headers must be ASCII-safe. - JSON and XML — These text formats have no native way to embed raw bytes, so binary fields are Base64-encoded into strings.
How the Encoding Works Step by Step
Let's encode the string Hi! into Base64 manually.
Step 1: Convert to ASCII Bytes
H = 72 = 01001000
i = 105 = 01101001
! = 33 = 00100001
Step 2: Concatenate the Bits
01001000 01101001 00100001
That's 24 bits total (3 bytes).
Step 3: Split into 6-Bit Groups
010010 | 000110 | 100100 | 100001
Step 4: Map to Base64 Characters
| 6-Bit Value | Decimal | Base64 Character |
|---|---|---|
010010 | 18 | S |
000110 | 6 | G |
100100 | 36 | k |
100001 | 33 | h |
Result: Hi! encodes to SGkh.
What About Padding?
When the input isn't a multiple of 3 bytes, zero bits are appended to complete the final 6-bit group, and = characters are added to pad the output to a multiple of 4:
- 1 byte left over → 2 Base64 chars +
== - 2 bytes left over → 3 Base64 chars +
= - 0 bytes left over → no padding needed
For example, encoding Hi (just 2 bytes):
H = 01001000, i = 01101001
Concatenated: 01001000 01101001
Pad to 18 bits: 01001000 01101001 00
6-bit groups: 010010 | 000110 | 100100
Base64 chars: S G k
Add padding: S G k =
Result: SGk=
Tip: The padding = characters don't carry data — they simply signal how many trailing zero bits were added so the decoder knows the exact original byte count.
Base64 vs Base64URL
Standard Base64 uses + and / in its alphabet. That's fine for email and most contexts, but those characters have special meaning in URLs and filenames:
+is interpreted as a space in URL query strings/is a path separator=is used for key-value pairs in query parameters
The Base64URL variant (defined in RFC 4648) fixes this with two substitutions and typically omits padding:
| Character | Standard Base64 | Base64URL |
|---|---|---|
| Index 62 | + | - |
| Index 63 | / | _ |
| Padding | = (required) | Omitted (optional) |
When to Use Which
- Standard Base64 — email (MIME), PEM certificates, data URIs, general binary-to-text encoding
- Base64URL — JWT tokens, URL query parameters, filenames, anything that passes through a URL parser
Tip: JWTs always use Base64URL without padding. If you try to decode a JWT payload with a standard Base64 decoder, you'll get errors or garbage unless you first replace - with + and _ with /, then re-add padding.
Common Use Cases
Base64 appears in more places than most developers realise:
Embedding Images in CSS/HTML
/* Inline a small icon to avoid an extra HTTP request */
.icon {
background-image: url(data:image/png;base64,iVBORw0KGgo...);
}
This is ideal for small images (under a few KB). For larger assets, a regular file is more efficient because the browser can cache it separately.
API Authentication Headers
# HTTP Basic Auth: username:password in Base64
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
JWT Payloads
A JWT token like eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.xxx contains three Base64URL-encoded segments: the header, the payload, and the signature.
Email Attachments
When you attach a PDF to an email, your mail client Base64-encodes the file and embeds it in the MIME message body with a Content-Transfer-Encoding: base64 header.
Storing Binary in JSON
{
"name": "document.pdf",
"content": "JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZw..."
}
APIs that accept file uploads via JSON payloads commonly expect a Base64-encoded string for the binary content.
Base64 Is NOT Encryption
Warning: Base64 is an encoding, not encryption. It provides zero security. Anyone can decode a Base64 string instantly — no key, no password, no secret required.
This is one of the most common misconceptions in web development. Developers sometimes Base64-encode sensitive data — API keys, passwords, tokens — thinking it obscures the value. It doesn't. Base64 is trivially reversible. It's a lookup table, not a cipher.
Consider HTTP Basic Auth: the credentials admin:secret123 become YWRtaW46c2VjcmV0MTIz. Anyone who sees that header can decode it in milliseconds. That's why Basic Auth must always be used over HTTPS — the transport encryption (TLS) provides the actual security, not the Base64 encoding.
Rules to live by:
- Never store passwords as Base64 — use bcrypt, scrypt, or Argon2
- Never use Base64 to "hide" API keys in client-side code — use environment variables and server-side proxies
- Never assume a Base64-encoded JWT payload is private — anyone with the token can read it
Command-Line Examples
Both macOS and Linux have built-in tools for Base64 encoding and decoding.
Using base64
# Encode a string (macOS and Linux)
echo -n "Hello, World!" | base64
# Output: SGVsbG8sIFdvcmxkIQ==
# Decode a Base64 string
echo -n "SGVsbG8sIFdvcmxkIQ==" | base64 --decode
# Output: Hello, World!
# Encode a file
base64 -i photo.png -o photo.b64
# Decode a file
base64 --decode -i photo.b64 -o photo.png
Tip: Always use echo -n (no trailing newline) when encoding. Without -n, the newline character becomes part of the encoded data, producing a different result than you'd expect.
Using openssl
# Encode
echo -n "Hello, World!" | openssl base64
# Output: SGVsbG8sIFdvcmxkIQ==
# Decode
echo -n "SGVsbG8sIFdvcmxkIQ==" | openssl base64 -d
# Output: Hello, World!
Quick Base64URL Conversion in the Shell
# Standard Base64 → Base64URL (remove padding, swap chars)
echo -n "Hello, World!" | base64 | tr '+/' '-_' | tr -d '='
# Output: SGVsbG8sIFdvcmxkIQ
# Base64URL → Standard Base64 (restore for decoding)
echo -n "SGVsbG8sIFdvcmxkIQ" | tr '-_' '+/' | awk '{
pad = (4 - length % 4) % 4;
for(i=0;i<pad;i++) printf "=";
print
}' | base64 --decode
Size Overhead
Base64 encoding always increases the data size by approximately 33%. Here's why:
Every 3 input bytes (24 bits) become 4 Base64 characters (each representing 6 bits). So the ratio is 4/3 — a 33.3% increase. A 1 MB file becomes roughly 1.33 MB when Base64-encoded.
| Original Size | Base64 Size | Overhead |
|---|---|---|
| 1 KB | 1.33 KB | +33% |
| 100 KB | 133 KB | +33% |
| 1 MB | 1.33 MB | +33% |
| 10 MB | 13.3 MB | +33% |
This overhead matters when you're embedding large assets inline. A 50 KB image as a data URI adds roughly 17 KB of bloat compared to serving it as a separate cached file. For small icons and sprites (under 2–4 KB), the trade-off is usually worth it to save an HTTP round-trip. For anything larger, serve it as a separate file.
Warning: Base64-encoded data in JSON APIs can be deceptive. A 10 MB video uploaded as a Base64 string in a JSON body becomes a 13.3 MB payload — and the entire JSON must be held in memory to parse. For large files, always prefer multipart form uploads.
Encode and Decode Instantly
BoltKit's Base64Lab tool lets you encode and decode Base64 on the fly — with support for the URL-safe variant, auto-detection, and instant results. Free on iPhone and iPad.
Get BoltKit Free