Base64 Encoding & Decoding: Complete Guide

Base64 is one of those technologies you encounter constantly as a developer — in email attachments, data URIs, JWTs, API payloads, and config files. This guide explains how it works, how to use it correctly in JavaScript and Python, and the one mistake that trips up nearly everyone: confusing encoding with encryption.

⚡ Try the Tool

Encode text or files to Base64 instantly — auto-detect, drag-and-drop, and byte-size stats.

Open Base64 Encoder & Decoder →

What Is Base64?

Base64 is a binary-to-text encoding scheme. It takes any binary data — a file, a string, raw bytes — and represents it as a string of printable ASCII characters. The name "Base64" refers to the 64 characters used: A–Z (26), a–z (26), 0–9 (10), plus two extra characters typically + and /.

The algorithm works by treating every 3 bytes of input (24 bits) as a group and splitting them into four 6-bit chunks. Each 6-bit chunk is mapped to one of the 64 characters in the alphabet. If the input length isn't divisible by 3, one or two = padding characters are appended to make the output length a multiple of 4.

Input bytes Binary Base64 groups (6-bit) Output
M a n (3 bytes) 010011010110000101101110 010011 | 010110 | 000101 | 101110 TWFu
M a (2 bytes) 0100110101100001 010011 | 010110 | 0001(00) | pad TWE=
M (1 byte) 01001101 010011 | 01(0000) | pad | pad TQ==

The size overhead is always around 33%: 3 input bytes become 4 output characters. For a 1 MB file, the Base64 output is roughly 1.37 MB.

Why Is Base64 Used?

Binary data causes problems in systems designed to handle text. Null bytes, non-printable control characters, and encoding ambiguities break protocols like SMTP (email), HTTP headers, XML, and JSON. Base64 solves this by guaranteeing the output contains only printable ASCII characters that are safe everywhere.

Common use cases:

Base64 in JavaScript

Encoding ASCII text (simple)

For ASCII text, JavaScript's built-in btoa() function works directly:

// Encode
const encoded = btoa('Hello, World!');
console.log(encoded); // "SGVsbG8sIFdvcmxkIQ=="

// Decode
const decoded = atob('SGVsbG8sIFdvcmxkIQ==');
console.log(decoded); // "Hello, World!"

Encoding UTF-8 text (emoji, international characters)

btoa() only handles Latin-1 characters. For Unicode strings (emoji, Chinese, Arabic, etc.), you must encode to UTF-8 bytes first:

// Encode UTF-8 text to Base64
function toBase64(str) {
    const bytes = new TextEncoder().encode(str);
    const binary = Array.from(bytes, b => String.fromCharCode(b)).join('');
    return btoa(binary);
}

// Decode Base64 back to UTF-8 text
function fromBase64(b64) {
    const binary = atob(b64);
    const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
    return new TextDecoder().decode(bytes);
}

// Test with emoji
console.log(toBase64('Hello 🌍'));   // "SGVsbG8g8J+MjQ=="
console.log(fromBase64('SGVsbG8g8J+MjQ==')); // "Hello 🌍"

Encoding a file to Base64 (browser)

To encode a file selected via <input type="file">:

function fileToBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.onload = () => {
            const bytes = new Uint8Array(reader.result);
            const binary = Array.from(bytes, b => String.fromCharCode(b)).join('');
            resolve(btoa(binary));
        };
        reader.onerror = reject;
    });
}

// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async () => {
    const b64 = await fileToBase64(input.files[0]);
    console.log(b64.substring(0, 50) + '...');
});

Encoding files in Node.js

const fs = require('fs');

// File to Base64
const fileBuffer = fs.readFileSync('./image.png');
const base64 = fileBuffer.toString('base64');
console.log(base64.substring(0, 50) + '...');

// Base64 to file
const decoded = Buffer.from(base64, 'base64');
fs.writeFileSync('./output.png', decoded);

Base64 in Python

Encoding and decoding strings

import base64

# Encode string to Base64
text = "Hello, World! 🌍"
encoded = base64.b64encode(text.encode('utf-8'))
print(encoded)  # b'SGVsbG8sIFdvcmxkISDwn4yN'

# Decode Base64 back to string
decoded = base64.b64decode(encoded).decode('utf-8')
print(decoded)  # "Hello, World! 🌍"

Encoding and decoding files

import base64

# File to Base64
with open('image.png', 'rb') as f:
    encoded = base64.b64encode(f.read())

# Save as text
with open('image.b64', 'w') as f:
    f.write(encoded.decode('ascii'))

# Base64 back to file
with open('image.b64', 'r') as f:
    data = base64.b64decode(f.read())

with open('output.png', 'wb') as f:
    f.write(data)

Standard Base64 vs. URL-Safe Base64

Standard Base64 uses + and / as characters 62 and 63. In URLs, + is interpreted as a space in query strings, and / separates path segments — breaking any Base64 string embedded in a URL.

URL-safe Base64 (RFC 4648 §5) replaces these:

VariantChars 62/63PaddingUsed In
Standard (RFC 4648 §4)+ and /=MIME, PEM, most Base64
URL-safe (RFC 4648 §5)- and _usually omittedJWTs, URL params, filenames
MIME (RFC 2045)+ and /=Email, 76-char line wrap required
// JavaScript: convert standard Base64 to URL-safe
function toBase64Url(b64) {
    return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// Python: built-in URL-safe variant
import base64
url_safe = base64.urlsafe_b64encode(data).rstrip(b'=')
decoded = base64.urlsafe_b64decode(url_safe + b'==')

MIME Line Wrapping

The MIME standard (RFC 2045) requires Base64-encoded data in email to be broken into lines of no more than 76 characters, each ending with CRLF (\r\n). This existed because some legacy email servers couldn't handle long lines.

Modern systems typically accept unwrapped Base64, but you'll still see line wrapping in:

// Add MIME line wrapping (76 chars)
function mimeWrap(b64) {
    return b64.match(/.{1,76}/g).join('\n');
}

// Python
import base64, textwrap
wrapped = '\n'.join(textwrap.wrap(
    base64.b64encode(data).decode(), 76
))

The #1 Mistake: Treating Base64 as Encryption

⚠ Security Warning

Base64 is not encryption. Anyone can decode it in seconds. Never use Base64 to "hide" passwords, API keys, tokens, or sensitive user data. You will get breached.

This mistake shows up constantly in codebases:

// ❌ Wrong — this is NOT security
const "hidden" = btoa('my_api_key_12345');
config.apiKey = hidden; // anyone can call atob() on it

// ✅ Right — use environment variables
const apiKey = process.env.API_KEY; // never in source code

Base64 provides zero confidentiality. It only changes the representation of data, not its accessibility. A developer who decodes your Base64-"encoded" API key will have your API key in under 5 seconds.

For actual security:

Performance Considerations

Base64 adds ~33% size overhead. For small payloads (under 100 KB) this rarely matters. For large files, it matters more:

The general rule: use Base64 for data under ~1 MB. For larger files, use direct file URLs, object storage (S3, R2), or streaming — not inline Base64.

💡 Quick Reference

btoa() = binary to ASCII = encode to Base64
atob() = ASCII to binary = decode from Base64
The "a" stands for ASCII, not "all" — easy to mix up.

Detecting Whether a String Is Base64

Auto-detecting Base64 isn't always reliable, but a good heuristic is:

function isBase64(str) {
    // Strip whitespace and line breaks (MIME-wrapped Base64)
    const clean = str.replace(/[\s\r\n]/g, '');
    if (clean.length === 0) return false;
    // Must only use Base64 alphabet
    if (!/^[A-Za-z0-9+/]*={0,2}$/.test(clean)) return false;
    // Length must be divisible by 4 after padding
    const padded = clean.padEnd(clean.length + (4 - clean.length % 4) % 4, '=');
    // Attempt decode to confirm it's valid
    try { atob(padded); return true; } catch { return false; }
}

This heuristic produces false positives for short strings that happen to only contain Base64 characters (e.g. the word "Hello" is valid Base64 syntax but it's probably plain text). Context matters — if the string is 128+ characters and uses the full alphabet, it's almost certainly Base64.

Summary

⚡ Encode or Decode Base64 Instantly

Paste text, drop a file, switch between encode/decode/auto — all client-side, no data sent anywhere.

Open Base64 Tool →