Base64 Encoding: The Complete Developer Guide
· Cosyslabs
Base64 encoding converts binary data into ASCII text using a 64-character alphabet, enabling safe transmission over protocols designed for text. Every three bytes of input become four Base64 characters, increasing size by approximately 33%. The encoding is completely reversible and provides no security — it is a data representation format, not encryption.
Why Developers Need Base64
Modern software frequently crosses boundaries between binary and text domains:
- HTTP headers accept only ISO-8859-1 printable characters
- JSON is a text format that cannot embed raw binary
- Email (SMTP) was designed for ASCII text
- HTML attributes are text strings
- Environment variables are strings
Base64 bridges these domains by representing any binary data as printable text.
The Encoding Algorithm
Base64 works by regrouping binary data from 8-bit bytes into 6-bit groups:
Step-by-Step
- Take input bytes and convert to binary
- Concatenate all bits into one stream
- Split into groups of 6 bits
- Map each 6-bit value (0–63) to a character in the Base64 alphabet
- If the input is not divisible by 3, pad with
=
The Base64 Alphabet
| Index | Char | Index | Char | Index | Char | Index | 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 | / |
Plus = for padding.
Worked Example
Input: "API"
Binary: 01000001 01010000 01001001
Groups: 010000 010101 000001 001001
Index: 16 21 1 9
Chars: Q V B J
Output: "QVBJ"
Padding
When input length is not a multiple of 3:
1 remaining byte → 2 Base64 chars + "=="
2 remaining bytes → 3 Base64 chars + "="
3 remaining bytes → 4 Base64 chars (no padding)
Standard vs URL-Safe Base64
Standard Base64 uses + and / as the 62nd and 63rd characters. These are reserved in URLs, so URL-safe Base64 substitutes them:
| Position | Standard | URL-Safe |
|---|---|---|
| 62 | + | - |
| 63 | / | _ |
| Padding | = | omitted or %3D |
JWT tokens use URL-safe Base64 without padding. Web browsers use standard Base64 in btoa()/atob().
// Standard Base64
btoa("Hello+World/Test");
// "SGVsbG8rV29ybGQvVGVzdA=="
// URL-safe (manual conversion)
function toUrlSafe(b64) {
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
function fromUrlSafe(b64url) {
const padded = b64url.replace(/-/g, "+").replace(/_/g, "/");
const pad = (4 - (padded.length % 4)) % 4;
return padded + "=".repeat(pad);
}
Language Reference
JavaScript / TypeScript
// Browser (strings only — no binary)
const encoded = btoa("Hello, World!");
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
// Node.js / browser (Uint8Array support)
const encoded = Buffer.from("Hello, World!").toString("base64");
const decoded = Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64").toString("utf8");
// URL-safe
const urlSafe = Buffer.from("Hello, World!").toString("base64url");
// Binary data (file contents)
import fs from "fs";
const fileBase64 = fs.readFileSync("image.png").toString("base64");
Python
import base64
# Encode string
encoded = base64.b64encode(b"Hello, World!").decode("utf-8")
# "SGVsbG8sIFdvcmxkIQ=="
# Decode
decoded = base64.b64decode("SGVsbG8sIFdvcmxkIQ==").decode("utf-8")
# URL-safe variant
url_safe = base64.urlsafe_b64encode(b"Hello+World").decode()
decoded_us = base64.urlsafe_b64decode(url_safe + "==") # Add padding if needed
Go
import "encoding/base64"
// Encode
encoded := base64.StdEncoding.EncodeToString([]byte("Hello, World!"))
// Decode
decoded, err := base64.StdEncoding.DecodeString("SGVsbG8sIFdvcmxkIQ==")
// URL-safe
urlEncoded := base64.URLEncoding.EncodeToString([]byte("Hello+/World"))
// URL-safe without padding
rawURL := base64.RawURLEncoding.EncodeToString([]byte("Hello"))
Rust
use base64::{engine::general_purpose, Engine as _};
let encoded = general_purpose::STANDARD.encode(b"Hello, World!");
let decoded = general_purpose::STANDARD.decode("SGVsbG8sIFdvcmxkIQ==").unwrap();
// URL-safe
let url_safe = general_purpose::URL_SAFE_NO_PAD.encode(b"Hello+World");
Data URIs
Data URIs embed file contents directly in HTML or CSS:
data:[<mediatype>][;base64],<data>
<!-- Inline PNG -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Logo">
<!-- Inline SVG -->
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxuczp4bG..." alt="Icon">
<!-- Inline font in CSS -->
@font-face {
font-family: "CustomFont";
src: url("data:font/woff2;base64,d09GMgABAAA...") format("woff2");
}
When to use data URIs:
- Small images (< 2 KB) that would otherwise cause an HTTP request
- Critical above-the-fold images in SSG/SSR pages
- SVG icons inlined in CSS for zero request overhead
When not to use data URIs:
- Large images — 33% overhead compounds with size; a 100 KB PNG becomes 137 KB
- Images shared across pages — no browser caching between pages
- Images that should be lazy loaded
HTTP Basic Authentication
HTTP Basic Auth Base64-encodes username:password in the Authorization header:
Authorization: Basic dXNlcjpwYXNzd29yZA==
Decoded: user:password
This is not encryption. The credentials are fully recoverable by anyone who intercepts the header. Always use HTTPS with Basic Auth.
MIME Email Encoding
SMTP transfers email over text channels. MIME encodes attachments using Base64:
Content-Type: application/pdf; name="report.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="report.pdf"
JVBERi0xLjQKJeLjz9MKNiAwIG9iago8PAovVHlwZSAvUGFnZQovUGFyZW50IDEgMCBSCi9N
ZWRpYUJveCBbMCAwIDYxMiA3OTJdCi9Db250ZW50cyA3IDAgUgo+PgplbmRvYmoK...
MIME Base64 wraps lines at 76 characters — a requirement that browser btoa() and most Base64 libraries do not apply by default.
JWT and Base64URL
JSON Web Tokens consist of three Base64URL-encoded segments:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMifQ.signature
To decode the payload (middle segment):
function decodeJwtPayload(token) {
const [, payload] = token.split(".");
// Restore standard Base64 from URL-safe
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
// Add padding
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
return JSON.parse(atob(padded));
}
Use the JWT Decoder Tool for quick inspection without writing code.
Performance and Size Impact
Base64 encoding always increases size by exactly 4/3 (≈ 33.3%):
| Original | Base64 Size |
|---|---|
| 3 bytes | 4 chars (4 bytes) |
| 1 KB | 1.37 KB |
| 100 KB | 137 KB |
| 1 MB | 1.37 MB |
Combined with gzip/brotli compression, Base64-encoded content compresses better than binary (text compresses better than binary in LZ-based algorithms), partially offsetting the overhead. Base64 in gzip is approximately 10–15% larger than raw binary in gzip.
Security Considerations
- Base64 is not encryption — it provides zero confidentiality
- Never use Base64 to "obscure" passwords, API keys, or sensitive data in client-side code
- The
Authorization: Basicheader requires HTTPS - Base64-encoded data in URLs (query params) is visible in server logs and browser history
Tools
- Base64 Encoder/Decoder — encode/decode text, files, and generate data URIs
- JWT Decoder — decode JWT headers and payloads
- URL Encoder — percent-encode URLs and components