What is Base64 encoding?
Base64 is a binary-to-text encoding scheme that represents binary data using a 64-character alphabet of printable ASCII letters. The standard is defined in RFC 4648, which formalizes both the "standard" alphabet (used in MIME, PEM certificates, and most APIs) and a URL-safe variant. Base64 was created in 1987 to solve a specific problem: how to send 8-bit binary data — images, executables, certificates — through 7-bit ASCII channels like email and early text-based protocols that would corrupt anything outside the printable range.
The 64-character alphabet is the union of three groups: uppercase letters A–Z (26), lowercase letters a–z (26), digits 0–9 (10), plus two extras — + and / for standard Base64, or - and _ for URL-safe Base64. Plus an optional = padding character (technically the 65th symbol, but never represents data). Every Base64 character encodes exactly 6 bits of input, so 3 bytes of binary input (24 bits) fit perfectly into 4 Base64 characters. This 3-to-4 ratio explains the famous 33% size overhead — 1 KB of binary becomes 1.33 KB of Base64.
Despite being a simple encoding (not encryption), Base64 is everywhere: JSON Web Tokens (JWTs) use Base64URL for header, payload, and signature; HTTP Basic Auth wraps username:password in Base64; PEM-encoded certificates and SSH keys are Base64; email attachments use Base64 inside MIME; data URIs (data:image/png;base64,...) inline images in HTML and CSS. Knowing exactly when Base64 helps versus when it hurts (it's not encryption, it's not compression, it inflates payloads) is a core developer skill.
How Base64 encoding works under the hood
The algorithm is a simple bit-regrouping. Imagine encoding the 3-byte ASCII string "Sun":
| Step | Value |
|---|---|
| Input characters | S u n |
| ASCII / byte values | 83 117 110 |
| Binary (3 × 8 bits = 24 bits) | 01010011 01110101 01101110 |
| Re-grouped into 6-bit chunks | 010100 110111 010101 101110 |
| Decimal values of each 6-bit chunk | 20 55 21 46 |
| Map to Base64 alphabet | U 3 V u |
| Final Base64 output | U3Vu |
Padding rules — why you see = at the end
Base64 always emits multiples of 4 characters. If your input length isn't divisible by 3, the encoder pads the missing bytes with zero bits and signals the padding with = characters at the end:
- 1 byte input → 2 Base64 chars +
==(e.g."M"→"TQ==") - 2 byte input → 3 Base64 chars +
=(e.g."Ma"→"TWE=") - 3 byte input → 4 Base64 chars, no padding (e.g.
"Man"→"TWFu")
Some implementations omit padding (RFC 4648 §3.2 allows it). When decoding, modern libraries handle both padded and unpadded inputs; older implementations may require =. If you see a "Invalid base64" error, try adding = to round the length up to a multiple of 4.
Standard vs URL-safe Base64 — the two variants
Standard Base64 uses + and / for the last two alphabet positions. Both are reserved characters in URLs, query strings, and filenames. So a Base64 string like aGVsbG8/d29ybGQ+ embedded into a URL would be misinterpreted: ? becomes a query delimiter, + becomes a space in form-encoded contexts. RFC 4648 §5 defines a URL-safe variant that swaps these with URL-friendly substitutes:
| Position | Standard (RFC 4648 §4) | URL-safe (RFC 4648 §5) |
|---|---|---|
| 62 (0x3E) | + | - |
| 63 (0x3F) | / | _ |
| Padding | = (required by some, optional by others) | = usually omitted |
When to use which:
- Standard Base64 — email attachments (MIME), PEM certificates, HTTP Basic Auth, traditional APIs, anywhere the output isn't going into a URL or filename.
- URL-safe Base64 — JWTs (mandatory per RFC 7515), URL parameters, OAuth flows, filenames, cookies, form fields.
+ → -, / → _, and strip trailing =. The reverse works to decode URL-safe Base64 with a standard decoder. This tool's "URL-safe" toggle does the substitution automatically.
When to use Base64 — and when not to
Good fits for Base64
- JWT (JSON Web Tokens) — header and payload are JSON objects, Base64URL-encoded so they survive transport in HTTP headers, cookies, and URL query parameters.
- HTTP Basic Auth — the
Authorization: Basic ...header carriesusername:passwordBase64-encoded. Not encryption, just encoding for HTTP-safe transport. Use HTTPS to actually protect the credentials. - PEM certificates & keys —
-----BEGIN CERTIFICATE-----wraps Base64-encoded DER bytes. The text format makes them paste-friendly across systems. - MIME email attachments — SMTP was designed for 7-bit text; Base64 lets binary attachments survive intact.
- Tiny inline images (data URIs) — for icons under 4 KB, a
data:image/svg+xml;base64,...URL eliminates an HTTP request. Use sparingly: the 33% overhead and lack of caching make this counterproductive at larger sizes. - Embedding binary in JSON or XML — when you can't use multipart upload and absolutely must put binary in a structured text field.
- QR code data — when encoding binary payloads (digital business cards, encrypted handshakes).
Bad fits for Base64
- Encryption substitute. Base64 is reversible by anyone in 1 millisecond. Encoding API keys or passwords in Base64 is security theater.
- Compression. Base64 increases size by 33%. If size matters, gzip the binary first, then Base64 the gzipped output.
- Large file uploads. Inflating a 10 MB image to 13.3 MB Base64 string just to put it in JSON wastes bandwidth and parser memory. Use multipart/form-data uploads instead.
- Long-term storage of large blobs. Database columns of Base64 BLOBs cost ~33% more storage and slow scans. Store raw bytes (BLOB / BYTEA) and encode at the API edge if needed.
Base64 in 8 programming languages
JavaScript / Browser
// ASCII / Latin-1 only — for full Unicode see below
btoa("hello world"); // → "aGVsbG8gd29ybGQ="
atob("aGVsbG8gd29ybGQ="); // → "hello world"
// Unicode-safe encode (modern browsers)
const utf8 = new TextEncoder().encode("café 中文");
const b64 = btoa(String.fromCharCode(...utf8));
// URL-safe Base64
const urlSafe = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// File → Base64 (FileReader, async)
const reader = new FileReader();
reader.onload = () => console.log(reader.result.split(',')[1]);
reader.readAsDataURL(file); // produces "data:...;base64,XXXXXX"
Node.js
// Buffer — Unicode-safe by default
const b64 = Buffer.from("hello world").toString('base64'); // "aGVsbG8gd29ybGQ="
const dec = Buffer.from(b64, 'base64').toString(); // "hello world"
// URL-safe variant (Node 16+)
const urlSafe = Buffer.from(data).toString('base64url');
// Encode a file
import { readFile } from 'node:fs/promises';
const b64File = (await readFile('image.png')).toString('base64');
Python
import base64
# Standard
encoded = base64.b64encode(b"hello world").decode() # 'aGVsbG8gd29ybGQ='
decoded = base64.b64decode("aGVsbG8gd29ybGQ=").decode() # 'hello world'
# URL-safe (no padding by convention; add back for decode if needed)
url_safe = base64.urlsafe_b64encode(b"hello").rstrip(b'=').decode()
# 'aGVsbG8' (5-byte input = 1 padding char stripped)
# Encode a file
with open('image.png', 'rb') as f:
b64_file = base64.b64encode(f.read()).decode()
PHP
// Standard Base64
$encoded = base64_encode("hello world"); // "aGVsbG8gd29ybGQ="
$decoded = base64_decode("aGVsbG8gd29ybGQ="); // "hello world"
// URL-safe (manual swap — PHP has no built-in)
function base64UrlEncode(string $data): string {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64UrlDecode(string $data): string {
return base64_decode(strtr($data, '-_', '+/'));
}
// Encode a file
$b64File = base64_encode(file_get_contents('image.png'));
Java
import java.util.Base64;
// Standard
String encoded = Base64.getEncoder().encodeToString("hello world".getBytes());
byte[] decoded = Base64.getDecoder().decode(encoded);
// URL-safe (no padding)
String urlSafe = Base64.getUrlEncoder().withoutPadding().encodeToString(data);
byte[] back = Base64.getUrlDecoder().decode(urlSafe);
// File → Base64
byte[] bytes = Files.readAllBytes(Paths.get("image.png"));
String b64 = Base64.getEncoder().encodeToString(bytes);
Go
import "encoding/base64"
// Standard
encoded := base64.StdEncoding.EncodeToString([]byte("hello world"))
decoded, _ := base64.StdEncoding.DecodeString(encoded)
// URL-safe (no padding)
urlSafe := base64.RawURLEncoding.EncodeToString([]byte("hello"))
decoded, _ = base64.RawURLEncoding.DecodeString(urlSafe)
Rust
use base64::{engine::general_purpose, Engine as _};
// Standard
let encoded = general_purpose::STANDARD.encode(b"hello world");
let decoded = general_purpose::STANDARD.decode(encoded)?;
// URL-safe, no padding
let url_safe = general_purpose::URL_SAFE_NO_PAD.encode(b"hello");
Bash
# Standard encode / decode
echo -n "hello world" | base64 # "aGVsbG8gd29ybGQ="
echo -n "aGVsbG8gd29ybGQ=" | base64 -d # "hello world"
# Encode a file (single line, no wrapping)
base64 -w 0 image.png > image.b64
# Decode back to a file
base64 -d image.b64 > image.png
# URL-safe variant via tr
echo -n "data" | base64 | tr '+/' '-_' | tr -d '='
Data URIs — embedding files inline
A data URI packs a small file directly into a URL using Base64. Browsers, mail clients, and most image renderers treat it like a normal URL but read the bytes inline instead of fetching them. The format:
data:[<mediatype>][;base64],<data>
// Example: 1×1 transparent PNG
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgAAIAAAUAAeImBZsAAAAASUVORK5CYII=
// SVG (often smaller without Base64)
data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'>...</svg>
When data URIs make sense
- Single-pixel tracking GIFs and 1×1 placeholders (under 100 bytes).
- Small icons in CSS where avoiding an HTTP request matters more than caching (under 4 KB rule of thumb).
- Email signatures with embedded logos that must work offline.
- Generated previews (e.g.
FileReader.readAsDataURLfor image upload preview).
When NOT to use them
- Anything larger than 4 KB — the 33% Base64 overhead plus the inability to cache the asset hurts more than it helps. Use a regular HTTP request.
- Production images that appear on multiple pages — caching wins.
- Anything that needs to be lazy-loaded or preloaded — data URIs can't be.
Encode Base64 online — common queries answered
Variations of "encode base64" all route to the same workflow: paste text or drop a file, copy the encoded string. The most common targeted use cases users land on this page for:
Base64 encode an image (PNG, JPG, SVG, WebP)
Drop any image file onto the upload zone above and the encoder reads it locally with FileReader, produces the standard Base64 string, and (optionally) wraps it in a data:image/png;base64, URI ready for inline use. The encoding happens in-browser — your image bytes never leave the device, which matters for screenshots that contain sensitive UI or unreleased designs. For the resulting data URI, embed it directly in CSS background-image: url(data:image/png;base64,…) or in HTML <img src="data:image/png;base64,…">. Avoid this pattern for images larger than ~10 KB — Base64 inflates payload by ~33%, and inline images can't be cached separately by the browser.
Base64 encoders — Standard (RFC 4648 §4) vs URL-safe (RFC 4648 §5)
Two valid Base64 alphabets, and the choice matters: Standard uses + and / as the 63rd and 64th characters and pads with =; URL-safe swaps to - and _ and often drops padding. Use Standard for HTTP body content, email MIME, and database BLOB columns. Use URL-safe in JWT segments, URL path/query parameters, and file names — anywhere a literal + or / would be reinterpreted by a parser. The toggle above switches between both alphabets without re-encoding the source bytes.
Base64encoder vs base64encode — naming, not behavior
The unified search query "base64encoder" (no space) and the imperative "base64 encode" map to the same intent. Some services brand themselves as "Base64Encoder", others as "Base64 encoder online", but the underlying operation is RFC 4648 §4. The encoder above accepts text or files, runs locally, and produces output identical to base64 on macOS/Linux, certutil -encode on Windows, and btoa() in the browser console (for ASCII strings only — btoa chokes on Unicode without a UTF-8 pre-encode pass). For the inverse, paste any Base64 string in and switch to Decode mode.
Decode Base64 to text or download as binary file
The decoder accepts both alphabets and auto-detects which one it received. Plain text decodes to text; binary content (an image, PDF, ZIP) is restored to bytes and offered as a download with the correct MIME type when the input is a data: URI. Pair the decoder with the hash generator when you need to verify a Base64-encoded checksum (e.g. SRI integrity hashes are sha384-{base64-encoded-sha384}). To embed Base64 in URLs without the trailing = padding causing 404s in some routers, switch to URL-safe mode.
Base64 best practices
- Don't confuse Base64 with encryption. Base64 is reversible without a key. If you need confidentiality, use AES-GCM or libsodium and Base64 the ciphertext if you need to embed it as text.
- Pick URL-safe Base64 by default for new APIs. It works in URLs, JSON, JWTs, cookies, and filenames without escaping. Standard Base64 needs additional URL encoding.
- Always decode in the same encoding as you encoded. Mixing standard with URL-safe is the #1 cause of "Invalid base64" errors. Most modern libraries auto-detect, but don't rely on it.
- For UTF-8 strings in JavaScript browsers, never use raw
btoa(string)on Unicode — it throws on characters above 0xFF. Usebtoa(unescape(encodeURIComponent(s)))or the modernTextEncoderapproach. - For files larger than ~5 MB in browsers, use
FileReaderwith chunked processing.readAsDataURLon huge files freezes the UI thread. - Validate decoded payloads before trusting them. Base64 will happily decode garbage into more garbage. After decoding a JWT, verify the signature; after decoding an image, check magic bytes.