Base64 Encoder & Decoder
What Base64 Actually Does
Base64 encodes arbitrary binary data as text using 64 printable characters. It exists because a great many systems — email headers, JSON documents, URLs, HTTP headers, XML attributes — are specified to carry text, and handing them raw bytes leads to corruption the moment a byte happens to look like a control character or a delimiter.
The mechanism is simple arithmetic. Three bytes are 24 bits; 24 bits split evenly into four 6-bit groups; each 6-bit group indexes one of 64 characters. Type Man into our binary translator and you get 01001101 01100001 01101110; regroup those 24 bits as 010011 010110 000101 101110 and you get indexes 19, 22, 5 and 46, which are T, W, F and u, so the Base64 output is TWFu. That three-to-four ratio is why Base64 output is always about 33% larger than its input, and why the encoded length is always a multiple of four once padding is applied. When the input length is not divisible by three, the final group is padded with = characters to complete the block.
Base64 vs Base64URL
Standard Base64 uses + and / as its final two characters. Both are problematic in URLs: / is a path separator and + is interpreted as a space in query strings. Base64URL, defined in RFC 4648 §5, substitutes - and _ instead, and usually drops the = padding because = also carries meaning in query strings.
| Aspect | Base64 | Base64URL |
|---|---|---|
| Character 62 | + | - |
| Character 63 | / | _ |
| Padding | =, usually required | Usually omitted |
| Safe in a URL | No | Yes |
| Typical use | Email, data URIs, HTTP Basic auth | JWTs, JWKs, URL parameters |
This tool detects which alphabet your input uses when decoding, and tells you rather than silently normalising it. A string containing characters from both alphabets is rejected outright, because that is not valid in either variant and almost always means two encoded values were concatenated by mistake.
The UTF-8 Trap
This is the defect that separates a correct Base64 tool from a broken one. In JavaScript, the built-in btoa() function operates on "binary strings" — strings in which every character represents exactly one byte. Any character above U+00FF throws an exception. Pass it an emoji, a Chinese character, or even an accented Latin letter, and it fails.
The correct approach is to convert the text to UTF-8 bytes first, then Base64-encode those bytes. That is what this tool does, which is why Grüße and rocket emoji encode and decode without corruption. Many online encoders skip this step and either throw an error or, worse, silently mangle the text.
// Broken — throws on any non-ASCII input
btoa("Grüße");
// Correct — encode to UTF-8 bytes first
const bytes = new TextEncoder().encode("Grüße");
btoa(String.fromCharCode(...bytes));
The byte counts shown above the output make this visible. A rocket emoji is a single character to a human but four bytes in UTF-8, and it is the byte count that determines the encoded length.
Where You Will Meet Base64
- JSON Web Tokens. Every segment of a JWT is base64url-encoded JSON. Paste a single segment here to read it, or use our JWT decoder to handle the whole token at once.
- JSON Web Keys. The RSA modulus and EC coordinates in a JWKS are base64url-encoded big-endian integers — see our JWKS decoder for the structure around them.
- HTTP Basic authentication. The
Authorizationheader carriesbase64(username:password), which is exactly why Basic auth over plain HTTP is indefensible: the credentials are one decode away. - Data URIs. Inlining a small image as
data:image/png;base64,…avoids an HTTP request at the cost of roughly 33% more bytes. The exception is a social share image: link-preview crawlers only fetch http(s) URLs, so a data URI inog:imageproduces a card with no picture — our Open Graph generator catches that. - Email attachments. MIME uses Base64 because SMTP was specified for 7-bit ASCII and cannot carry arbitrary bytes safely.
- Kubernetes secrets. Values in a Secret manifest are Base64-encoded, which is an encoding step for transport, not a security control — a point that causes real confusion.
The Base64 Alphabet
The 64 characters are not arbitrary — they were chosen because they survive every transport layer that mangles binary data. The full standard alphabet maps values 0–63 to A–Z, a–z, 0–9, +, and /. Base64URL replaces + with - and / with _ so the result is safe inside URLs, filenames, and XML attributes.
| Value | Char | Value | Char | Value | Char | Value | Char |
|---|---|---|---|---|---|---|---|
| 0 | A | 16 | Q | 32 | g | 48 | w |
| 1 | B | 17 | R | 33 | h | 49 | x |
| 2 | C | 18 | S | 34 | i | 50 | y |
| 3 | D | 19 | T | 35 | j | 51 | z |
| 4 | E | 20 | U | 36 | k | 52 | 0 |
| 5 | F | 21 | V | 37 | l | 53 | 1 |
| 6 | G | 22 | W | 38 | m | 54 | 2 |
| 7 | H | 23 | X | 39 | n | 55 | 3 |
| 8 | I | 24 | Y | 40 | o | 56 | 4 |
| 9 | J | 25 | Z | 41 | p | 57 | 5 |
| 10 | K | 26 | a | 42 | q | 58 | 6 |
| 11 | L | 27 | b | 43 | r | 59 | 7 |
| 12 | M | 28 | c | 44 | s | 60 | 8 |
| 13 | N | 29 | d | 45 | t | 61 | 9 |
| 14 | O | 30 | e | 46 | u | 62 | + / - |
| 15 | P | 31 | f | 47 | v | 63 | / / _ |
Values 62 and 63 show both variants: standard / base64url. Every other character is the same in both alphabets.
How to Base64 Encode in Every Language
Every major language has Base64 built in, but the API names differ and the UTF-8 trap exists in most of them. Here are correct examples that handle Unicode text.
JavaScript / Node.js
// Browser
const encoded = btoa(String.fromCharCode(
...new TextEncoder().encode("Grüße 🚀")
));
// Node.js
const encoded = Buffer.from("Grüße 🚀").toString("base64");
Python
import base64
encoded = base64.b64encode("Grüße 🚀".encode("utf-8")).decode("ascii")
decoded = base64.b64decode(encoded).decode("utf-8")
Java
import java.util.Base64;
String encoded = Base64.getEncoder()
.encodeToString("Grüße 🚀".getBytes("UTF-8"));
String decoded = new String(
Base64.getDecoder().decode(encoded), "UTF-8");
Bash / Command Line
# Encode
echo -n "Grüße 🚀" | base64
# Decode
echo "R3LDvMOfZSDwn5qA" | base64 --decode
Go
import "encoding/base64"
encoded := base64.StdEncoding.EncodeToString(
[]byte("Grüße 🚀"))
Common Base64 Mistakes
These are the errors that cause real bugs in production. Each one has appeared in code reviews and incident reports.
- Using Base64 for "security". Encoding a database password in Base64 and committing it to a repository is not protecting the password. Anyone who reads the file can decode it in seconds — this page does it without a key. Kubernetes Secrets store values as Base64 for the same transport reason as email, and the documentation explicitly says it is not a security mechanism.
- Double encoding. Encoding an already-encoded value produces valid Base64, but the result cannot be decoded in a single pass. This happens when a pipeline encodes at two stages without checking whether the input is already encoded. If the output looks correct but is 33% longer than expected, suspect double encoding.
- Mixing standard and URL-safe alphabets. Concatenating a standard Base64 value and a Base64URL value produces a string that is invalid in both variants. This tool detects that and rejects it rather than silently mangling the output.
- Forgetting to encode to UTF-8 first. The
btoa()function in browsers throws on any character above U+00FF. Code that works in tests with ASCII input fails the first time a user enters an accent or emoji. Always convert to UTF-8 bytes before Base64-encoding. - Stripping whitespace from the input. Many Base64 values are line-wrapped at 76 characters (MIME standard) or at other intervals. Decoders must ignore whitespace, and this one does. Rejecting line breaks causes valid values — especially PEM certificates and email attachments — to fail.
Reading Padding
The = characters at the end are not decoration; they signal how many bytes the final group actually holds. One = means the last group decodes to two bytes, and two = means it decodes to one. Because that information is recoverable from the string length alone, many systems omit padding entirely — which is why base64url values in JWTs rarely have any.
This decoder accepts input with or without padding and restores it internally. It does reject a string whose length leaves a remainder of one when divided by four, because no valid Base64 string can have that length; encountering it means characters were lost or added in transit.
Base64 in Cron Jobs and Automation
Passing binary data or multi-line config through environment variables and shell scripts is fragile — a stray newline or special character breaks quoting. Base64 solves this cleanly: encode the value, pass the safe ASCII string, decode on the other side. This pattern is common in CI/CD pipelines, Docker entrypoints, and cron jobs that need to pass structured data to a script without worrying about shell escaping.
# Store a multi-line config as a single env var
export CONFIG_B64=$(echo '{"db":"prod","replicas":3}' | base64)
# In the script, decode it
echo "$CONFIG_B64" | base64 --decode | jq .
Decoding Binary Data
Not everything encoded in Base64 is text. Images, compressed archives, and cryptographic keys are all commonly Base64-encoded, and decoding them produces bytes that are not valid UTF-8. Rather than displaying replacement characters and pretending it worked, this tool detects that case, tells you the content is binary, and shows a hex dump of the bytes instead. The reported byte count is accurate either way.
Base64 Size Calculator
Because every 3 input bytes become 4 output characters, you can predict the encoded size before running the encoder. For an input of n bytes, the padded output is 4 × ⌈n / 3⌉ characters. Without padding, it is ⌈4n / 3⌉ characters. In practice, Base64 adds about 33-37% overhead depending on padding and line wrapping.
| Input size | Encoded size (padded) | Overhead |
|---|---|---|
| 1 byte | 4 chars | +300% |
| 3 bytes | 4 chars | +33% |
| 100 bytes | 136 chars | +36% |
| 1 KB | 1,368 chars | +34% |
| 1 MB | 1,398,104 chars | +33% |
This overhead matters when embedding images as data URIs in CSS or HTML. A 10 KB icon becomes about 13.3 KB as Base64 — acceptable for small assets, but impractical for large images where a separate HTTP request is cheaper. Converting between data formats? Try our YAML↔JSON converter or XML↔JSON converter for structured data instead.