Base64 Guide

What Is Base64 Encoding? A Complete Guide

By S. Nisikant·Published ·Updated ·13 min read

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 .pem files 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: TWFu

The 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.

IdxChrIdxChrIdxChrIdxChr
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

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 Base64Base64URLReason
+-Unsafe in URLs (interpreted as space in form data)
/_Unsafe in URLs and Unix file paths
= (padding)OmittedUnsafe 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 Decoder

Common 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 YWxpY2U6c2VjcmV0MTIz

PEM 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 Encoder

Encoding & 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

LanguageEncodeDecodeBase64URL
JS (browser)btoa(str)atob(str)— (manual replace)
JS (Node.js)Buffer.from(data).toString('base64')Buffer.from(str, 'base64')toString('base64url')
Pythonbase64.b64encode(bytes)base64.b64decode(str)base64.urlsafe_b64encode()
Gobase64.StdEncoding.EncodeToString(b)base64.StdEncoding.DecodeString(s)base64.URLEncoding
JavaBase64.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 Formatter

Base64 vs Hex

Both Base64 and hexadecimal (Base16) are binary-to-text encodings, but they make different trade-offs between density and readability:

FeatureBase64Hex
Bits per character64
Output size vs binary~133% (33% overhead)200% (100% overhead)
Alphabet size64 characters16 characters
Human readabilityOpaqueSomewhat (bytes as 2-char pairs)
URL-safe by defaultNo (use Base64URL)Yes
Common usesJWT, images, email, API payloadsHashes, 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.Error

Fix

# 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 characters

Fix

// Encode Unicode to UTF-8 bytes first
const bytes = new TextEncoder().encode('Hello 👋');
const b64 = btoa(String.fromCharCode(...bytes));
// Decode: atob, then TextDecoder

btoa() 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); // garbled

Fix

// 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.