Base64 encodes arbitrary bytes into 64 ASCII symbols
so that binary data can travel through text-only systems
(emails, JSON, URLs, HTML, etc.).
Therefore, it is NOT encryption, it is representation.
A–Z a–z 0–9 + / (URL-safe variant uses - and _ instead of + and /).= as padding (except some URL/MIME variants where padding may be omitted).Standard alphabet (index → char)
0–25 : A–Z
26–51 : a–z
52–61 : 0–9
62 : +
63 : /
= if input length mod 3 is 1 (add ==) or 2 (add =).Example: "Man" (ASCII: 0x4D 0x61 0x6E)
0x4D 0x61 0x6E
01001101 01100001 01101110 → 24 bits
Split 6-bit groups:
010011 010110 000101 101110 → 19, 22, 5, 46
Map to alphabet:
19=T, 22=W, 5=F, 46=u → "TWFu"
// Minimal JS example
const encode = (s) => btoa(unescape(encodeURIComponent(s))); // UTF-8 safe
const decode = (b) => decodeURIComponent(escape(atob(b)));
console.log(encode("Man")); // "TWFu"
console.log(decode("TWFu")); // "Man"
+ → -, / → _; padding = often omitted.| Variant | 62 | 63 | Padding | Typical uses |
|---|---|---|---|---|
| Standard | + | / | = required | MIME, general |
| URL-safe | - | _ | = often omitted | JWT, URLs, web APIs |
| MIME | + | / | = required | Email, attachments |
// Base64url helpers (JS)
const toUrl = (b64) => b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/,"");
const fromUrl = (u) => (u + "===".slice((u.length + 3) % 4)).replace(/-/g,"+").replace(/_/g,"/");
base64 standard lib)import base64
s = "你好, world"
b = s.encode("utf-8")
e = base64.b64encode(b).decode("ascii")
print(e) # 5L2g5aW9LCDkuK3lm70=
# decode
raw = base64.b64decode(e)
print(raw.decode("utf-8"))
// Node.js
const buf = Buffer.from("hello", "utf8");
console.log(buf.toString("base64")); // aGVsbG8=
console.log(Buffer.from("aGVsbG8=", "base64").toString("utf8")); // hello
// Browser (UTF-8 safe)
const enc = (s) => btoa(unescape(encodeURIComponent(s)));
const dec = (b) => decodeURIComponent(escape(atob(b)));
= padding (esp. base64url). Re-pad to multiple of 4.NO_NL options).4/3 of input. Large payloads inflate, consider compression first.# Quick CLI (Linux/macOS)
printf "hello" | base64 # aGVsbG8=
echo "aGVsbG8=" | base64 --decode # hello
# OpenSSL (no newlines)
printf "hello" | openssl base64 -A
4 * ceil(n_bytes / 3)floor(n_b64_chars * 3 / 4)data:<mime>;base64,<payload>
img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
= padding characters?= tells the decoder how many bytes to keep:
== → last block had 1 input byte= → last block had 2 input bytes= → last block had 3 input bytes"M" → 0x4D)Input bytes (1): 4D
Pad with 16 zero bits to reach 24 bits:
01001101 00000000 00000000
Split to 6-bit groups:
010011 010000 000000 000000
19 16 0 0 → naive alphabet: T Q A A
Naive Base64 would be "TQAA" (ambiguous).
Correct Base64 replaces the last two chars with "=" to mark missing bytes:
"TQ=="
"Ma" → 0x4D 0x61)Input bytes (2): 4D 61
Pad with 8 zero bits:
01001101 01100001 00000000
Split to 6-bit groups:
010011 010110 000100 000000
19 22 4 0 → naive alphabet: T W E A
Naive Base64 would be "TWEA".
Correct Base64 uses one "=" to mark one missing byte:
"TWE="
= characters are explicit metadata that makes decoding unambiguous.= is often omitted for compactness.
To decode, restore padding so length % 4 == 0 (append = as needed).// Re-pad a Base64url string u back to standard Base64 for decoding
const repad = (u) => (u + "===".slice((u.length + 3) % 4))
.replace(/-/g, "+")
.replace(/_/g, "/");
// Examples:
repad("TQ") // "TQ==" (1 input byte)
repad("TWE") // "TWE=" (2 input bytes)
repad("TWFu") // "TWFu" (3 input bytes, no padding)