base64encodinggopythonjavascript

Base64 Encoding Explained — Standard, URL-safe, and Raw Variants

·8 min read

What is Base64?

Base64 is a binary-to-text encoding scheme that represents arbitrary binary data using only 64 printable ASCII characters: A–Z (26), a–z (26), 0–9 (10), and two additional characters (+ and / in standard; - and _ in URL-safe).

Key point: Base64 is encoding, not encryption. Anyone can decode Base64 trivially — there is no secret key. Its purpose is safe transmission of binary data (images, files, binary protocols) over channels that only support text: HTTP headers, JSON strings, email (MIME), XML attributes, and HTML data: URIs.

A 3-byte input (24 bits) becomes 4 Base64 characters (6 bits each). This means Base64 increases data size by approximately 33%.

The Three Base64 Variants

VariantChar 62Char 63PaddingRFCWhen to use
Standard+/=RFC 4648 §4Email, file transfer, non-URL contexts
URL-safe-_=RFC 4648 §5URL query params, file names
Raw URL-safe-_NoneRFC 4648 §5JWT tokens, OAuth tokens

The + and / in Standard Base64 are URL-unsafe — + means space in form encoding and / is the URL path separator. URL-safe Base64 replaces them to avoid this ambiguity.

JavaScript Base64

javascript
// Browser / Node.js — built-in btoa/atob (text only, NOT binary-safe)
const encoded = btoa("Hello, World!");           // "SGVsbG8sIFdvcmxkIQ=="
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");  // "Hello, World!"

// For binary data in Node.js, use Buffer
const buf = Buffer.from("Hello, World!");
const b64 = buf.toString("base64");             // Standard Base64
const urlSafe = buf.toString("base64url");      // URL-safe (Node 16+)

// Decode Base64 back to string
const back = Buffer.from(b64, "base64").toString("utf8");

// URL-safe to standard conversion (manual, if needed)
const toStandard = (s) => s.replace(/-/g, "+").replace(/_/g, "/");
const toUrlSafe = (s) => s.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");

// Encode an image file to Base64 data URI
const { readFileSync } = require("fs");
const imgData = readFileSync("logo.png");
const dataUri = `data:image/png;base64,${imgData.toString("base64")}`;

Go Base64 Examples

go
import (
    "encoding/base64"
    "fmt"
)

data := []byte("Hello, World!")

// Standard Base64 (RFC 4648 §4)
std := base64.StdEncoding.EncodeToString(data)
fmt.Println(std) // => "SGVsbG8sIFdvcmxkIQ=="

// URL-safe Base64 (RFC 4648 §5)
urlSafe := base64.URLEncoding.EncodeToString(data)
fmt.Println(urlSafe) // Same here — "Hello, World!" has no + or /

// Raw URL-safe (no padding — used in JWT)
rawURLSafe := base64.RawURLEncoding.EncodeToString(data)
fmt.Println(rawURLSafe) // => "SGVsbG8sIFdvcmxkIQ" (no ==)

// Decode
decoded, err := base64.StdEncoding.DecodeString(std)
if err != nil {
    panic(err)
}
fmt.Println(string(decoded)) // => Hello, World!

// Fix missing padding before decoding (common when receiving raw Base64)
func addPadding(s string) string {
    switch len(s) % 4 {
    case 2: return s + "=="
    case 3: return s + "="
    }
    return s
}

Python Base64 Examples

python
import base64

data = b"Hello, World!"

# Standard Base64
std = base64.b64encode(data).decode()
print(std)  # => "SGVsbG8sIFdvcmxkIQ=="

# URL-safe Base64
url_safe = base64.urlsafe_b64encode(data).decode()
print(url_safe)  # => "SGVsbG8sIFdvcmxkIQ==" (same for this input)

# Raw URL-safe (strip padding)
raw = base64.urlsafe_b64encode(data).decode().rstrip("=")
print(raw)  # => "SGVsbG8sIFdvcmxkIQ"

# Decode (add padding back if missing)
def decode_base64(s: str) -> bytes:
    padding = 4 - len(s) % 4
    if padding != 4:
        s += "=" * padding
    return base64.urlsafe_b64decode(s)

# Encode a file to Base64
with open("image.png", "rb") as f:
    img_b64 = base64.b64encode(f.read()).decode()
    data_uri = f"data:image/png;base64,{img_b64}"

Common Use Cases

JWT tokens use Raw URL-safe Base64 (no padding) for all three parts: header.payload.signature

HTML data URIs use Standard Base64: <img src="data:image/png;base64,iVBORw0...">

Email attachments (MIME) use Standard Base64 with line breaks every 76 characters per RFC 2045

HTTP Basic Authentication encodes username:password as Standard Base64 in the Authorization: Basic header

JSON APIs use Base64 to embed binary fields (e.g., a thumbnail image or cryptographic signature) in a JSON string field

Common Mistakes and Pitfalls

Wrong variant — the most common mistake:

javascript
// Standard Base64 in a URL — + and / break URL parsing
const encoded = btoa("some data with +/chars");
const url = `/api/resource/${encoded}`; // WRONG — + and / are URL-special

// URL-safe Base64 for URLs
const safe = Buffer.from("some data").toString("base64url"); // CORRECT

Missing padding when decoding:

Raw Base64 strings (from JWT, OAuth tokens) have no = padding. Decoders expecting padding will throw. Add back the = characters:

javascript
function decodeRawBase64(raw) {
  const padded = raw + "=".repeat((4 - raw.length % 4) % 4);
  return Buffer.from(padded, "base64").toString("utf8");
}

Using btoa() with binary data in the browser:

btoa() only handles Latin-1 (ISO-8859-1) strings. For UTF-8 text or binary data use Buffer in Node.js or TextEncoder + manual Base64 in the browser.

Size increase — 33% overhead:

A 1 MB binary file encodes to ~1.33 MB of Base64. For large file transfers, use binary protocols instead of Base64-embedding in JSON when possible.

When to Use Each Variant

ContextVariantWhy
JWT header/payload/signatureRaw URL-safe= padding breaks .-separated format
OAuth tokens, PKCE verifierRaw URL-safeURL-safe, no padding needed
Email attachments (MIME)StandardRFC 2045 requires standard Base64
HTML data: URIStandardStandard Base64 only
URL query parametersURL-safeAvoids + and / conflicts
File namesURL-safe/ in filename is a path separator
JSON string field (binary data)StandardAny variant works; standard is most common

Use JSONKit's Base64 Encoder/Decoder for instant browser-based encoding and decoding of any text or file — no installation required.

Try Base64 Encoder / Decoder

Encode or decode Base64 — Standard, URL-safe, and Raw variants. Code snippets for 5 languages.