How to Encode and Decode Base64
A practical guide to Base64 encoding and decoding — understand when and why to use it, how to handle Unicode, and how to do it in JavaScript, Python, and the command line.
What is Base64 and why is it used?
Base64 is an encoding scheme that converts binary data (or arbitrary text) into a string of 64 printable ASCII characters: A–Z, a–z, 0–9, +, and /. It is used whenever binary data needs to travel through a channel designed for text — email attachments (MIME), data URLs in HTML/CSS, embedding images in JSON APIs, and HTTP Basic Auth headers.
How Base64 works
Base64 takes 3 bytes (24 bits) of input at a time and splits them into four 6-bit groups. Each 6-bit group maps to one of the 64 printable characters. If the input is not a multiple of 3 bytes, one or two = padding characters are added to make the output length a multiple of 4.
Input: H e l
Bytes: 72 101 108
Binary: 01001000 01100101 01101100
6-bit: 010010 000110 010101 101100
Base64: S G V s
Result: SGVsEncoding in JavaScript
The browser's built-in btoa() encodes and atob() decodes. For Unicode strings, you need to encode to UTF-8 bytes first — plain btoa() only handles Latin-1 characters.
// ASCII-safe strings
btoa("Hello"); // "SGVsbG8="
atob("SGVsbG8="); // "Hello"
// Unicode-safe (UTF-8)
function encodeUTF8(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
(_, p1) => String.fromCharCode("0x" + p1)));
}
function decodeUTF8(b64) {
return decodeURIComponent(atob(b64).split("").map(
c => "%" + c.charCodeAt(0).toString(16).padStart(2, "0")).join(""));
}
encodeUTF8("नमस्ते"); // works correctlyEncoding in Python
Python's base64 module is in the standard library. Strings must be encoded to bytes before encoding.
import base64
# Encode
message = "Hello, world!"
encoded = base64.b64encode(message.encode("utf-8")).decode("utf-8")
print(encoded) # "SGVsbG8sIHdvcmxkIQ=="
# Decode
decoded = base64.b64decode(encoded).decode("utf-8")
print(decoded) # "Hello, world!"Encoding from the command line
On macOS and Linux, the base64 command is available in the terminal. On macOS, base64 -D decodes; on GNU/Linux, use base64 -d.
# Encode
echo -n "Hello, world!" | base64
# SGVsbG8sIHdvcmxkIQ==
# Decode (macOS)
echo "SGVsbG8sIHdvcmxkIQ==" | base64 -D
# Decode (Linux)
echo "SGVsbG8sIHdvcmxkIQ==" | base64 -dBase64 URL-safe variant
Standard Base64 uses + and / which have special meaning in URLs. The URL-safe variant replaces + with - and / with _, and often omits padding. This is the form used inside JWTs.
// Standard Base64 → URL-safe
function toUrlSafe(b64) {
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
// URL-safe → Standard Base64
function fromUrlSafe(b64url) {
const pad = b64url.length % 4 === 0 ? "" : "=".repeat(4 - b64url.length % 4);
return b64url.replace(/-/g, "+").replace(/_/g, "/") + pad;
}Does Base64 compress data?
No — it expands it. Base64 output is roughly 33% larger than the input because three bytes become four characters.
Is Base64 encryption?
No. Base64 is a reversible encoding with no secret key. Anyone can decode it. Do not use it to obscure sensitive data.
Why does atob() throw for some Unicode strings?
atob() expects each character to be in the Latin-1 range (code points 0–255). Characters outside that range — like emoji or CJK characters — throw a DOMException. You need to encode to UTF-8 bytes first using encodeURIComponent.
How to URL Encode and Decode
A practical guide to percent-encoding — understand which characters need encoding, how the % notation works, and how to encode and decode URLs in JavaScript, Python, and the terminal.
Read guide →How HTML Escaping Works
A practical guide to HTML escaping — why it matters for security, which characters to escape, when to use it, and how XSS happens when you skip it.
Read guide →