What Is Base64 Encoding? A Complete Guide
You have seen it everywhere: the three-part string inside a JWT, the data:image/png;base64,… in a CSS file, the scrambled-looking string in an email attachment, or the credentials hidden inside an Authorization: Basic header. All of these are Base64 encoding — a scheme that converts any binary data into a safe, printable ASCII string. This guide explains how it works, why it exists, and how to use it correctly in your code.
What Is Base64?
Base64 is a binary-to-text encoding scheme that represents binary data using only 64 printable ASCII characters. It is defined in RFC 4648 and is one of the most widely used encodings in computing.
The core problem Base64 solves: many protocols and systems were designed to handle text, not arbitrary binary data. Email (SMTP), HTTP headers, URLs, HTML attributes, and JSON strings all have restrictions on which bytes they can safely carry. Base64 bridges this gap by converting any sequence of bytes into a string of letters, digits, and two symbols — characters that are safe in virtually every text-based system.
Two things Base64 is not:
- Not encryption — anyone can decode a Base64 string instantly without any key. It provides no confidentiality.
- Not compression — Base64 output is approximately 33% larger than the input, because every 3 bytes becomes 4 characters.
A Brief History of Base64
Base64's roots lie in the challenge of sending binary files over email systems built exclusively for 7-bit ASCII text.
- 1980s — PEM (Privacy Enhanced Mail): Early Internet email security protocols used a 64-character encoding to represent cryptographic keys and certificates in text files. This “PEM encoding” is the direct ancestor of modern Base64 and is still visible in
.pemfiles today. - 1992 — RFC 1341 (MIME): The Multipurpose Internet Mail Extensions standard formally defined Base64 as a content-transfer encoding, enabling email clients to attach binary files (images, documents, executables) to plain-text email messages.
- 1996 — RFC 2045: Updated the MIME specification and cemented Base64 as the standard binary-to-text encoding for email attachments, defining the 76-character line-length limit still used in MIME today.
- 2006 — RFC 4648: The canonical, consolidated standard for Base64 and its URL-safe variant (Base64URL), as well as Base32 and Base16. RFC 4648 is the authoritative reference for modern implementations.
With the rise of the web, Base64 escaped email and became universal: data URIs for inline images, JWTs for stateless authentication, PKCE for OAuth security, and binary payloads in REST APIs all rely on Base64 or its URL-safe variant.
How Base64 Works
Base64 works by treating the input as a stream of bits and re-grouping those bits into 6-bit chunks. Since 2⁶ = 64, each 6-bit group maps to exactly one character from the Base64 alphabet.
The key ratio: 3 input bytes (24 bits) → 4 output characters (4 × 6 bits).
Worked Example: “Man”
Input text: M a n
ASCII bytes: 77 97 110
Binary: 01001101 01100001 01101110
Regroup into 6-bit chunks:
010011 010110 000101 101110
Decimal: 19 22 5 46
Base64 chars: T W F u
Result: TWFuThe three bytes 77, 97, 110 are concatenated into a 24-bit stream: 010011010110000101101110. Split into four 6-bit groups gives decimal values 19, 22, 5, 46, which map to T, W, F, u in the Base64 alphabet.
To decode, the process reverses: each character is looked up in the alphabet to get its 6-bit value, the bits are concatenated, then re-split into 8-bit bytes.
The Base64 Alphabet
The 64 characters are: uppercase A–Z (indices 0–25), lowercase a–z (indices 26–51), digits 0–9 (indices 52–61), and the symbols + (62) and / (63). The = character is used for padding but is not part of the 64-character alphabet itself.
| Idx | Chr | Idx | Chr | Idx | Chr | Idx | Chr |
|---|---|---|---|---|---|---|---|
| 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 | / |
All 64 characters are printable, non-whitespace ASCII and are safe in every text protocol. This is why Base64 has remained the dominant binary-to-text encoding for over three decades.
Padding with =
Base64 processes input in 3-byte groups. When the input length is not a multiple of 3, padding is added to make the output length a multiple of 4 characters.
Input: 3 bytes (e.g. "ABC" = 3 bytes)
→ Produces 4 Base64 chars, no padding needed
Result: QUJD
Input: 2 bytes (e.g. "AB" = 2 bytes)
→ Produces 3 Base64 chars + 1 padding character
Result: QUI=
Input: 1 byte (e.g. "A" = 1 byte)
→ Produces 2 Base64 chars + 2 padding characters
Result: QQ==The = characters are not encoded data — they are a signal to the decoder that the last group was short. Some implementations (particularly Base64URL) omit padding entirely, relying on the decoder to infer it from the string length.
To add back omitted padding: the total length must be a multiple of 4. Append = characters until length % 4 === 0 (adding 0, 1, or 2 characters — never 3, since a 3-char remainder is impossible in valid Base64).
Base64URL Variant
Standard Base64 uses + and /, which have special meanings in URLs — they become %2B and %2F when percent-encoded, making the output unwieldy in URLs, filenames, and HTTP headers.
Base64URL (RFC 4648 §5) solves this with two character substitutions and optional padding removal:
| Standard Base64 | Base64URL | Reason |
|---|---|---|
| + | - | Unsafe in URLs (interpreted as space in form data) |
| / | _ | Unsafe in URLs and Unix file paths |
| = (padding) | Omitted | Unsafe in some URL parsers and query strings |
Base64URL is mandatory wherever tokens appear in URLs or HTTP headers: JWT header and payload segments, OAuth 2.0 PKCE code_challenge, FIDO2/WebAuthn credential IDs, and URL-safe file attachments.
Decode JWT Base64URL Segments
JWTs are made of three Base64URL-encoded segments. Paste any JWT into the free JWT Decoder to instantly decode the header and payload — entirely in your browser.
Open JWT DecoderCommon Use Cases
Images and Binary Assets in HTML/CSS
Base64 lets you embed binary files directly in HTML or CSS as data URIs, eliminating extra HTTP requests for small assets:
<!-- Inline PNG image in HTML -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="icon" />
/* Inline SVG background in CSS */
.icon {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0i...");
}Best for small icons and logos (under ~5 KB). For larger images, the 33% size overhead and loss of browser caching make separate files more efficient.
JSON Web Tokens (JWT)
Every JWT consists of three Base64URL-encoded segments: the header (algorithm), the payload (claims), and the signature. The segments are visible to anyone who decodes them — only the signature prevents tampering.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Base64URL(header JSON)
.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTcxNjQyMDAwMH0 ← Base64URL(payload JSON)
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Base64URL(signature)HTTP Basic Authentication
The HTTP Basic Auth scheme transmits credentials as Authorization: Basic <base64(username:password)>. This is encoding, not encryption — always use HTTPS with Basic Auth, as the credentials are trivially decodable.
# username:password → Base64
echo -n 'alice:secret123' | base64
# YWxpY2U6c2VjcmV0MTIz
# HTTP header
Authorization: Basic YWxpY2U6c2VjcmV0MTIzPEM Certificates and Cryptographic Keys
TLS certificates, RSA private keys, and public keys are binary DER-encoded structures wrapped in Base64 for storage and transport in .pem files:
-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END CERTIFICATE-----Binary Data in JSON APIs
JSON has no native binary type. When an API needs to return image bytes, file content, or cryptographic material, it Base64-encodes the data into a JSON string field:
{
"filename": "avatar.png",
"content_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAABAA..."
}Generate QR Codes Instantly
QR codes encode binary data using Base64 internally. Use the free QR Encoder to generate QR codes for URLs, text, or contact cards — all processed in your browser.
Open QR EncoderEncoding & Decoding in Code
Every mainstream language ships with Base64 support. Always use the built-in or a trusted library — implementing Base64 from scratch is error-prone and slower.
JavaScript — Browser
// Encode a string to Base64
const encoded = btoa('Hello, World!');
console.log(encoded); // SGVsbG8sIFdvcmxkIQ==
// Decode Base64 to string
const decoded = atob('SGVsbG8sIFdvcmxkIQ==');
console.log(decoded); // Hello, World!
// ⚠️ btoa/atob only handle Latin-1 (bytes 0-255)
// For Unicode / emoji, encode as UTF-8 bytes first:
function toBase64(str) {
const bytes = new TextEncoder().encode(str);
return btoa(String.fromCharCode(...bytes));
}
function fromBase64(b64) {
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
return new TextDecoder().decode(bytes);
}
console.log(toBase64('Hello 👋')); // SGVsbG8g8J+Ruw==JavaScript — Node.js
import { readFileSync, writeFileSync } from 'node:fs';
// Encode a string
const encoded = Buffer.from('Hello, World!').toString('base64');
// SGVsbG8sIFdvcmxkIQ==
// Decode Base64 to string
const decoded = Buffer.from('SGVsbG8sIFdvcmxkIQ==', 'base64').toString('utf8');
// Hello, World!
// Encode a binary file to Base64
const fileBytes = readFileSync('image.png');
const base64File = fileBytes.toString('base64');
// Decode Base64 back to binary file
const originalBytes = Buffer.from(base64File, 'base64');
writeFileSync('output.png', originalBytes);
// Base64URL (URL-safe, omits + / =)
const base64url = Buffer.from('Hello+/World').toString('base64url');Python
import base64
# Encode bytes → Base64 bytes
encoded = base64.b64encode(b'Hello, World!')
print(encoded) # b'SGVsbG8sIFdvcmxkIQ=='
# Decode Base64 → bytes
decoded = base64.b64decode(b'SGVsbG8sIFdvcmxkIQ==')
print(decoded) # b'Hello, World!'
# Encode string → Base64 string
b64_str = base64.b64encode(b'Hello').decode('ascii')
# SGVsbG8=
# Base64URL (URL-safe: + → -, / → _)
url_safe = base64.urlsafe_b64encode(b'ûï¾')
print(url_safe) # b'--++' (differs from standard b64)
decoded_url = base64.urlsafe_b64decode(url_safe)
# Read file and encode
with open('image.png', 'rb') as f:
b64 = base64.b64encode(f.read()).decode('ascii')Library Quick Reference
| Language | Encode | Decode | Base64URL |
|---|---|---|---|
| JS (browser) | btoa(str) | atob(str) | — (manual replace) |
| JS (Node.js) | Buffer.from(data).toString('base64') | Buffer.from(str, 'base64') | toString('base64url') |
| Python | base64.b64encode(bytes) | base64.b64decode(str) | base64.urlsafe_b64encode() |
| Go | base64.StdEncoding.EncodeToString(b) | base64.StdEncoding.DecodeString(s) | base64.URLEncoding |
| Java | Base64.getEncoder().encodeToString(b) | Base64.getDecoder().decode(s) | Base64.getUrlEncoder() |
Format & Inspect Decoded Payloads
After decoding a Base64 string that contains JSON, paste it into the JSON Formatter to pretty-print, validate, and explore the structure.
Open JSON FormatterBase64 vs Hex
Both Base64 and hexadecimal (Base16) are binary-to-text encodings, but they make different trade-offs between density and readability:
| Feature | Base64 | Hex |
|---|---|---|
| Bits per character | 6 | 4 |
| Output size vs binary | ~133% (33% overhead) | 200% (100% overhead) |
| Alphabet size | 64 characters | 16 characters |
| Human readability | Opaque | Somewhat (bytes as 2-char pairs) |
| URL-safe by default | No (use Base64URL) | Yes |
| Common uses | JWT, images, email, API payloads | Hashes, binary dumps, colour codes |
Rule of thumb: use Base64 when you need compact binary-to-text encoding for protocols (JWT, email, APIs). Use hex when you need humans to be able to read and compare individual bytes — cryptographic hashes, debug dumps, colour codes.
Best Practices
- ✓
1. Never use Base64 for security
Base64 is not encryption. Storing passwords, API keys, or secrets as Base64 provides zero protection — it is instantly reversible by anyone. Use proper hashing (bcrypt, Argon2) for passwords, and encryption (AES-256-GCM) for secrets at rest.
- ✓
2. Use Base64URL for URLs, JWTs, and filenames
Standard Base64 characters + and / are unsafe in URLs, HTTP query strings, and file paths. Always use the Base64URL variant (- and _ instead of + and /) when the output will appear in a URL or be used as a filename.
- ✓
3. Strip whitespace before decoding
MIME-formatted Base64 (RFC 2045) inserts \r\n line breaks every 76 characters. If you receive Base64 from email content, PEM files, or line-wrapped sources, strip all whitespace before passing to the decoder.
- ✓
4. Prefer binary transports over Base64 when possible
If your protocol supports raw binary — HTTP/2, WebSockets, gRPC, multipart form uploads — send binary directly rather than Base64. This avoids the 33% size overhead and reduces CPU cost from encoding and decoding.
Common Errors
Invalid character in Base64 string
Problem
// Base64URL string passed to standard decoder
const decoded = atob('eyJhbGciOiJIUzI1NiJ9'); // may have - or _
// → DOMException: Failed to execute 'atob'Fix
// Convert Base64URL → Base64 before decoding
const b64 = base64url
.replace(/-/g, '+')
.replace(/_/g, '/');
const decoded = atob(b64);Standard Base64 uses + and /; Base64URL replaces them with - and _. If you receive a JWT or URL-encoded value, convert the characters before passing to a standard decoder.
Incorrect padding / Invalid Base64 length
Problem
# JWT payload segment — no = padding by default
payload = "eyJzdWIiOiJ1c2VyXzEyMyJ9"
import base64
base64.b64decode(payload) # may raise binascii.ErrorFix
# Add padding to make length a multiple of 4
payload = "eyJzdWIiOiJ1c2VyXzEyMyJ9"
padding = 4 - len(payload) % 4
decoded = base64.b64decode(payload + '=' * (padding % 4))Base64URL strings often omit the = padding. Add the right number of = characters (0–3) so the total length is divisible by 4, or use a library that handles padding automatically.
btoa: String contains characters outside Latin1
Problem
// btoa only handles bytes 0–255
const encoded = btoa('Hello 👋');
// → InvalidCharacterError: The string contains invalid charactersFix
// Encode Unicode to UTF-8 bytes first
const bytes = new TextEncoder().encode('Hello 👋');
const b64 = btoa(String.fromCharCode(...bytes));
// Decode: atob, then TextDecoderbtoa() and atob() operate on Latin-1 (bytes 0–255), not Unicode. For emoji or any string with characters above U+00FF, convert to UTF-8 bytes using TextEncoder first.
Decoded output contains garbage / unexpected bytes
Problem
// MIME Base64 adds \r\n every 76 characters
// Passing it directly to a decoder produces wrong output
const data = "SGVsbG8s\r\nIFdvcmxkIQ=="; // MIME-wrapped
const result = atob(data); // garbledFix
// Strip all whitespace before decoding
const clean = data.replace(/[\r\n\s]/g, '');
const result = atob(clean);MIME-formatted Base64 (RFC 2045) inserts a CRLF line break after every 76 characters. Always strip whitespace from Base64 strings before decoding, especially when handling email content or PEM files.
Frequently Asked Questions
- What is Base64 used for?
- Base64 is used whenever binary data must travel through a channel designed for text. Common examples: embedding images in HTML/CSS (data URIs), encoding JWT header and payload, HTTP Basic Authentication credentials, PEM-formatted cryptographic keys and certificates, MIME email attachments, and binary data in JSON API responses.
- Is Base64 encoding the same as encryption?
- No — Base64 is encoding, not encryption. Anyone can decode a Base64 string without a key. It converts binary data to ASCII text for safe transmission; it provides no confidentiality. If you need to protect data, use encryption (AES, RSA) — Base64 may then be used to represent the ciphertext as printable text.
- Why is it called Base64?
- Because it uses an alphabet of exactly 64 characters — A–Z (26), a–z (26), 0–9 (10), and two symbols (+ and /). Each Base64 character represents exactly 6 bits (2⁶ = 64), so three bytes (24 bits) map to exactly four Base64 characters.
- What is the difference between Base64 and Base64URL?
- Base64URL is a URL-safe variant defined in RFC 4648 §5. It replaces + with - and / with _ so the output can appear in URLs and filenames without percent-encoding. It also typically omits the = padding character. Base64URL is used in JWTs, OAuth PKCE code challenges, and URL query parameters.
- How much larger is Base64 output than the original data?
- Base64 output is approximately 33% larger than the input. Every 3 bytes of binary data becomes 4 Base64 characters. For example, a 3 KB binary file becomes roughly 4 KB when Base64-encoded. This overhead is why Base64 is used only when a text-only channel makes it necessary.
Decode & Inspect Tokens Online
Use our free browser tools to decode Base64-encoded JWTs, format JSON payloads, and generate QR codes — all processed locally, never sent to a server.