Base64 taucht überall auf, sobald man anfängt zu suchen — JWT-Tokens, Daten-URIs, E-Mail-Anhänge,
API-Payloads mit Binärdateien. Die Kodierung selbst ist in
RFC 4648
definiert und konzeptionell simpel: Nehmen Sie beliebige Bytes und repräsentieren Sie sie mit nur 64
druckbaren ASCII-Zeichen. Was Menschen stolpern lässt, ist die Implementierung in JavaScript —
verschiedene APIs in Browser vs. Node.js, die Unicode-Falle, die btoa() zum Werfen bringt,
und die URL-sichere Variante, von der JWTs abhängen. Dieser Leitfaden deckt alles mit funktionierendem Code ab.
btoa() und atob() im Browser
Der Browser hat
btoa()
und
atob()
schon lange. Die Namen sind verwirrend (binary to ASCII und zurück), aber die Verwendung ist
für einfache Strings unkompliziert:
// Encode a plain ASCII string
const encoded = btoa('hello world');
console.log(encoded); // "aGVsbG8gd29ybGQ="
// Decode it back
const decoded = atob('aGVsbG8gd29ybGQ=');
console.log(decoded); // "hello world"
// A more realistic example — encoding a simple auth token
const credentials = 'apiuser:s3cr3tkey';
const basicAuth = 'Basic ' + btoa(credentials);
// "Basic YXBpdXNlcjpzM2NyM3RrZXk="
// This is exactly what HTTP Basic Authentication usesbtoa() verarbeitet nur Strings, bei denen
jedes Zeichen einen Codepunkt ≤ 255 hat (der Latin-1-Bereich). Übergeben Sie einen String mit einem
Emoji oder nicht-lateinischen Zeichen und es wirft sofort einen InvalidCharacterError.
Das ist einer der häufigsten Base64-Bugs in Browser-Code.// ❌ This throws — emoji is outside Latin-1
btoa('Hello 🌍');
// Uncaught DOMException: Failed to execute 'btoa' on 'Window':
// The string to be encoded contains characters outside of the Latin1 range.
// ❌ This also throws — any non-ASCII character will do it
btoa('café');
// Uncaught DOMException: ...Unicode im Browser sicher verarbeiten
Die Lösung besteht darin, den String zuerst in UTF-8-Bytes zu kodieren und dann diese Bytes
Base64-zu-kodieren. Der klassische Ansatz verwendet encodeURIComponent und einen
Prozent-Decode-Trick. Der moderne Ansatz verwendet TextEncoder, der in
allen modernen Browsern
und Node.js 11+ verfügbar ist:
// ✅ Unicode-safe encode using TextEncoder
function encodeBase64(str) {
const bytes = new TextEncoder().encode(str); // UTF-8 byte array
const binString = Array.from(bytes, byte =>
String.fromCodePoint(byte)
).join('');
return btoa(binString);
}
// ✅ Unicode-safe decode using TextDecoder
function decodeBase64(base64Str) {
const binString = atob(base64Str);
const bytes = Uint8Array.from(binString, char =>
char.codePointAt(0)
);
return new TextDecoder().decode(bytes);
}
// Now emojis and international text work fine
console.log(encodeBase64('Hello 🌍')); // "SGVsbG8g8J+MjQ=="
console.log(decodeBase64('SGVsbG8g8J+MjQ==')); // "Hello 🌍"
console.log(encodeBase64('Héllo café')); // "SMOpbGxvIGNhZsOp"
console.log(decodeBase64('SMOpbGxvIGNhZsOp')); // "Héllo café"Bewahren Sie diese zwei Hilfsfunktionen irgendwo in Ihrer Codebasis auf und vergessen Sie,
dass nacktes btoa() existiert. Das TextEncoder/TextDecoder-Paar
ist das richtige Werkzeug für alles jenseits von reinem ASCII. Sie können es jetzt mit dem
Base64-Encoder-Tool ausprobieren.
Buffer.from() in Node.js
Node.js hat hierfür eine eigene API über die Buffer-Klasse, die Kodierung/Dekodierung sauberer handhabt. Es gibt keine Unicode-Falle hier, da Sie die Eingabekodierung explizit angeben:
// Encode string → Base64
const encoded = Buffer.from('Hello 🌍', 'utf8').toString('base64');
console.log(encoded); // "SGVsbG8g8J+MjQ=="
// Decode Base64 → string
const decoded = Buffer.from('SGVsbG8g8J+MjQ==', 'base64').toString('utf8');
console.log(decoded); // "Hello 🌍"
// Practical example — encoding a JSON payload to embed in a config file
const config = {
apiKey: 'sk-prod-abc123',
projectId: 'proj_x9f2k',
region: 'us-east-1'
};
const encodedConfig = Buffer.from(JSON.stringify(config), 'utf8').toString('base64');
// eyJhcGlLZXkiOiJzay1wcm9kLWFiYzEyMyIsInByb2plY3RJZCI6InByb2pfeDlmMmsiLCJyZWdpb24iOiJ1cy1lYXN0LTEifQ==
// Decode and parse it back
const decodedConfig = JSON.parse(
Buffer.from(encodedConfig, 'base64').toString('utf8')
);
console.log(decodedConfig.region); // "us-east-1"Beachten Sie, dass btoa() und atob() in Node.js 16+ auch als globale
Funktionen verfügbar sind (für Browser-Kompatibilität), aber die Buffer-API ist idiomatischer
in Node.js und existiert seit
Node.js v0.1.
Für JSON-spezifische Kodierung ist das JSON zu Base64-Tool für schnelle
manuelle Konvertierungen praktisch.
URL-sicheres Base64 — Was JWTs tatsächlich verwenden
Standard-Base64 verwendet + und / in seinem Alphabet. Beide Zeichen sind
in URLs besonders — + bedeutet ein Leerzeichen in Query-Strings und / ist ein
Pfadtrenner. Wenn Sie Base64 in einer URL oder als JWT-Segment benötigen, verwenden Sie die URL-sichere
Variante: Ersetzen Sie + durch - und / durch _,
dann entfernen Sie das =-Auffüllen. Dies ist in
RFC 4648 §5
standardisiert und wird intern von jeder JWT-Bibliothek verwendet:
// Convert standard Base64 to URL-safe Base64
function toBase64Url(base64Str) {
return base64Str
.replace(/+/g, '-')
.replace(///g, '_')
.replace(/=+$/, ''); // strip padding
}
// Convert URL-safe Base64 back to standard Base64
function fromBase64Url(base64UrlStr) {
// Restore padding — length must be a multiple of 4
const padded = base64UrlStr + '==='.slice((base64UrlStr.length + 3) % 4);
return padded
.replace(/-/g, '+')
.replace(/_/g, '/');
}
// Encode a string to URL-safe Base64
function encodeBase64Url(str) {
return toBase64Url(btoa(str));
}
// Decode URL-safe Base64 to a string
function decodeBase64Url(str) {
return atob(fromBase64Url(str));
}
// Example: manually inspect a JWT payload
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjQyLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MTM0MDAwMDB9.signature';
const [header, payload] = jwt.split('.');
console.log(decodeBase64Url(header));
// {"alg":"HS256","typ":"JWT"}
console.log(decodeBase64Url(payload));
// {"userId":42,"role":"admin","iat":1713400000}Deshalb sehen Sie Base64-Strings wie
eyJhbGciOiJIUzI1NiJ9 in JWTs — kein Auffüllen, Bindestriche statt Pluszeichen.
Beim Senden von kodierten Daten als URL-Query-Parameter sollten Sie immer die URL-sichere Variante
verwenden, um kaputte URLs zu vermeiden. Das Base64-Decoder-Tool
verarbeitet automatisch sowohl Standard- als auch URL-sicheres Base64.
Eine Datei mit der FileReader-API kodieren
Eine häufige Browser-Aufgabe: Der Benutzer wählt ein Bild oder ein Dokument aus, und Sie müssen
es als Base64 an eine API senden. Die
FileReader-API
hat readAsDataURL() genau dafür — sie liefert einen vollständigen Daten-URI mit dem
enthaltenen MIME-Typ:
// Wrap FileReader in a Promise for easier async usage
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
// result is "data:image/png;base64,iVBORw0KGgo..."
// Strip the data URI prefix to get just the Base64 string
const base64 = reader.result.split(',')[1];
resolve(base64);
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsDataURL(file);
});
}
// Hook it up to a file input
const fileInput = document.getElementById('avatarUpload');
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
try {
const base64 = await fileToBase64(file);
console.log(`File size: ${file.size} bytes`);
console.log(`Base64 length: ${base64.length} chars`);
// Send to your API
await fetch('/api/users/42/avatar', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image: base64, mimeType: file.type })
});
} catch (err) {
console.error('Upload failed:', err.message);
}
});Wenn Sie den vollständigen Daten-URI (einschließlich des MIME-Typ-Präfixes) anstelle von nur
dem rohen Base64 benötigen, lassen Sie .split(',')[1] weg und verwenden Sie
reader.result direkt. Für die Massenkonvertierung von Dateien verarbeitet das
Bild zu Base64-Tool Bilder ohne Code zu schreiben.
Binärdaten und Uint8Arrays kodieren
Manchmal beginnen Sie nicht mit einem String oder einer Datei — Sie haben rohe Bytes aus
einer WebCrypto-Operation, einem Canvas-Export oder einem WebAssembly-Modul. Hier ist, wie man in
beiden Umgebungen von einem Uint8Array zu Base64 und zurück gelangt:
// --- Browser ---
// Uint8Array → Base64 (browser)
function uint8ToBase64(bytes) {
const binString = Array.from(bytes, byte =>
String.fromCodePoint(byte)
).join('');
return btoa(binString);
}
// Base64 → Uint8Array (browser)
function base64ToUint8(base64Str) {
const binString = atob(base64Str);
return Uint8Array.from(binString, char => char.codePointAt(0));
}
// Example: export a canvas as raw PNG bytes → Base64
const canvas = document.getElementById('myCanvas');
canvas.toBlob(blob => {
blob.arrayBuffer().then(buffer => {
const bytes = new Uint8Array(buffer);
const encoded = uint8ToBase64(bytes);
console.log('PNG as Base64:', encoded.slice(0, 40) + '...');
});
}, 'image/png');
// --- Node.js ---
// Uint8Array / Buffer → Base64 (Node.js)
function uint8ToBase64Node(bytes) {
return Buffer.from(bytes).toString('base64');
}
// Base64 → Buffer (Node.js)
function base64ToBufferNode(base64Str) {
return Buffer.from(base64Str, 'base64');
}
// Example: hash a password and encode the result
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update('mySecretPassword').digest();
// hash is a Buffer (which extends Uint8Array)
console.log(hash.toString('base64'));
// "XohImNooBHFR0OVvjcYpJ3NgxxxxxxxxxxxxxA=="Bilder als Daten-URIs einbetten
Eine der praktischsten Verwendungen von Base64 in der Web-Entwicklung ist das direkte Einbetten von Bildern in HTML oder CSS, wodurch eine HTTP-Anfrage vermieden wird. Sie haben Daten-URIs wahrscheinlich in Inline-SVGs oder E-Mail-Vorlagen gesehen. Hier ist das Muster:
<!-- Inline image in HTML — no separate network request -->
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
alt="1x1 transparent pixel"
width="1"
height="1"
/>/* Inline background image in CSS — commonly used for small icons and loading spinners */
.spinner {
width: 32px;
height: 32px;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEyIDJhMTAgMTAgMCAxIDAgMCAyMCAxMCAxMCAwIDAgMCAwLTIweiIvPjwvc3ZnPg==");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}// Generate a data URI from a fetched image (Node.js)
const fs = require('fs');
const path = require('path');
function imageFileToDataUri(filePath) {
const ext = path.extname(filePath).slice(1).toLowerCase();
const mimeMap = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg',
gif: 'image/gif', svg: 'image/svg+xml', webp: 'image/webp' };
const mimeType = mimeMap[ext] ?? 'application/octet-stream';
const fileData = fs.readFileSync(filePath);
const base64 = fileData.toString('base64');
return `data:${mimeType};base64,${base64}`;
}
const dataUri = imageFileToDataUri('./logo.png');
// "data:image/png;base64,iVBORw0KGgo..."
// Drop this into an <img src> or CSS background-imageEin kompaktes Utility-Modul für beide Umgebungen
Anstatt btoa()-Aufrufe in Ihrer Codebasis zu verstreuen, lohnt es sich,
ein einzelnes Utility-Modul zu haben, das Unicode, URL-sichere Varianten abdeckt und sowohl in
Browser als auch Node.js funktioniert. Hier ist eines, das alles davon tut:
// base64.js — drop into any project
const isNode = typeof process !== 'undefined' && process.versions?.node;
export function encode(str) {
if (isNode) {
return Buffer.from(str, 'utf8').toString('base64');
}
// Browser: encode to UTF-8 bytes first, then Base64
const bytes = new TextEncoder().encode(str);
const binString = Array.from(bytes, b => String.fromCodePoint(b)).join('');
return btoa(binString);
}
export function decode(base64Str) {
if (isNode) {
return Buffer.from(base64Str, 'base64').toString('utf8');
}
// Browser: Base64 → bytes → UTF-8 string
const binString = atob(base64Str);
const bytes = Uint8Array.from(binString, c => c.codePointAt(0));
return new TextDecoder().decode(bytes);
}
export function encodeUrlSafe(str) {
return encode(str)
.replace(/+/g, '-')
.replace(///g, '_')
.replace(/=+$/, '');
}
export function decodeUrlSafe(str) {
const padded = str + '==='.slice((str.length + 3) % 4);
return decode(padded.replace(/-/g, '+').replace(/_/g, '/'));
}
export function encodeBytes(bytes) {
if (isNode) return Buffer.from(bytes).toString('base64');
const binString = Array.from(bytes, b => String.fromCodePoint(b)).join('');
return btoa(binString);
}
export function decodeToBytes(base64Str) {
if (isNode) return Buffer.from(base64Str, 'base64');
const binString = atob(base64Str);
return Uint8Array.from(binString, c => c.codePointAt(0));
}// Usage examples
import { encode, decode, encodeUrlSafe, decodeUrlSafe } from './base64.js';
encode('Hello 🌍'); // "SGVsbG8g8J+MjQ=="
decode('SGVsbG8g8J+MjQ=='); // "Hello 🌍"
encodeUrlSafe('[email protected]'); // "dXNlckBleGFtcGxlLmNvbQ" (no +, /, or =)
decodeUrlSafe('dXNlckBleGFtcGxlLmNvbQ'); // "[email protected]"Häufige Fallstricke
- btoa() wirft bei Nicht-Latin-Zeichen — jedes Zeichen über Codepunkt 255 verursacht
InvalidCharacterError. Verwenden Sie immer denTextEncoder-Ansatz oderBuffer.from(str, 'utf8')in Node.js. - Auffüllen ist wichtig für die Dekodierung — Base64-Strings müssen eine Länge haben, die ein Vielfaches von 4 ist. Fehlendes
=-Auffüllen lässtatob()je nach Browser still Müll zurückgeben oder werfen. Stellen Sie vor dem Dekodieren von URL-sicheren Strings immer das Auffüllen wieder her. - Buffer vs. String-Kodierung in Node.js —
Buffer.from(str)verwendet standardmäßig UTF-8, aberBuffer.from(str, 'binary')behandelt den String als Latin-1-Bytes. Die falsche Kodierung beim Dekodieren zu verwenden, produziert verstümmelte Ausgabe, die schwer zu debuggen sein kann. - Daten-URI-MIME-Typ —
data:;base64,...(ohne MIME-Typ) funktioniert in einigen Browsern, aber nicht in anderen. Geben Sie immer den MIME-Typ an:data:image/png;base64,.... - Zeilenumbrüche in MIME-Base64 — RFC 4648 erlaubt Implementierungen, alle 76 Zeichen Zeilenumbrüche einzufügen (wie E-Mail-Codierer es tun).
atob()undBuffer.from()verarbeiten dies beide, aber wenn Sie Base64 selbst generieren, fügen Sie keine Zeilenumbrüche hinzu, es sei denn, das Zielsystem erwartet sie.
Zusammenfassung
Base64 in JavaScript ist eines jener Themen, die trivial aussehen, bis sie einen beißen.
Die Kurzversion: Verwenden Sie niemals nacktes btoa() für benutzergenerierte Inhalte — umhüllen
Sie es mit TextEncoder, um Unicode korrekt zu verarbeiten. In Node.js ist
Buffer.from(str, 'utf8').toString('base64') das richtige Idiom. Wenn der kodierte String
in einer URL oder einem JWT landet, wechseln Sie zur URL-sicheren Variante. Für schnelle Experimente
oder einmalige Konvertierungen sparen die Tools
Base64-Encoder, Base64-Decoder
und JSON zu Base64 Zeit. Die
MDN Base64-Glossarseite
bietet auch eine solide browser-fokussierte Referenz, wenn Sie eine zweite Meinung zu einem dieser
Punkte benötigen.