Base64 duikt overal op zodra je begint te zoeken — JWT-tokens, data-URI's, e-mailbijlagen,
API-payloads met binaire bestanden. De codering zelf is gedefinieerd in
RFC 4648
en is conceptueel doodgewoon: neem willekeurige bytes en stel ze voor met slechts 64 afdrukbare
ASCII-tekens. Wat mensen struikelt is de implementatie in JavaScript — verschillende
API's in browser versus Node.js, de Unicode-valkuil waardoor btoa() een fout gooit,
en de URL-safe variant waarop JWT's vertrouwen. Deze gids behandelt alles met werkende code.
btoa() en atob() in de Browser
De browser heeft al lang
btoa()
en
atob().
De namen zijn verwarrend (binary to ASCII en andersom), maar het gebruik is
eenvoudig voor eenvoudige strings:
// 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() verwerkt alleen strings waarbij
elk teken een codepunt ≤ 255 heeft (het Latin-1-bereik). Geef het een string met
een emoji of niet-Latin-teken en het gooit onmiddellijk InvalidCharacterError.
Dit is een van de meest voorkomende Base64-bugs in browsercode.// ❌ 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 Veilig Verwerken in de Browser
De oplossing is om de string eerst naar UTF-8-bytes te coderen en die bytes dan in Base64 te coderen.
De klassieke aanpak gebruikt encodeURIComponent en een percent-decode-truc. De moderne
aanpak gebruikt TextEncoder, die
beschikbaar is in alle moderne browsers
en Node.js 11+:
// ✅ 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é"Bewaar deze twee hulpfuncties ergens in je codebase en vergeet dat kale
btoa() bestaat. Het TextEncoder/TextDecoder-paar is het
juiste gereedschap voor alles buiten puur ASCII. Je kunt het nu uitproberen met de
Base64 Encoder-tool.
Buffer.from() in Node.js
Node.js heeft zijn eigen API hiervoor via de Buffer-klasse, die codering/decodering schoner afhandelt. Er is hier geen Unicode-valkuil omdat je de invoercodering expliciet opgeeft:
// 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"Merk op dat btoa() en atob() ook beschikbaar zijn in Node.js 16+
als globalen (voor browsercompatibiliteit), maar de Buffer-API is meer idiomatisch in
Node.js en is er al sinds
Node.js v0.1.
Voor JSON-specifieke codering is de JSON to Base64-tool handig
voor snelle handmatige conversies.
URL-Safe Base64 — Wat JWT's Eigenlijk Gebruiken
Standaard Base64 gebruikt + en / in zijn alfabet. Beide tekens
zijn speciaal in URL's — + betekent een spatie in querystrings en /
is een padscheider. Wanneer je Base64 in een URL of als JWT-segment nodig hebt, gebruik je de URL-safe
variant: vervang + door - en / door _,
en verwijder de =-opvulling. Dit is gestandaardiseerd in
RFC 4648 §5
en is wat elke JWT-bibliotheek intern gebruikt:
// 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}Daarom zie je Base64-strings zoals
eyJhbGciOiJIUzI1NiJ9 in JWT's — geen opvulling, streepjes in plaats van plustekens.
Gebruik altijd de URL-safe variant bij het verzenden van gecodeerde data als URL-queryparameter om
kapotte URL's te vermijden. De Base64 Decoder-tool verwerkt zowel
standaard als URL-safe Base64 automatisch.
Een Bestand Coderen met de FileReader API
Een veelvoorkomende browsertaak: de gebruiker kiest een afbeelding of document en je moet het
als Base64 naar een API sturen. De
FileReader API
heeft readAsDataURL() precies hiervoor — het geeft je een complete data-URI met het
MIME-type inbegrepen:
// 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);
}
});Als je de volledige data-URI (inclusief het MIME-type-voorvoegsel) nodig hebt in plaats van alleen
de ruwe Base64, sla dan .split(',')[1] over en gebruik reader.result direct.
Voor massale bestandsconversie verwerkt de Image to Base64-tool
afbeeldingen zonder dat je code hoeft te schrijven.
Binaire Data en Uint8Arrays Coderen
Soms begin je niet met een string of een File — je hebt ruwe bytes van een
WebCrypto-operatie, een canvas-export of een WebAssembly-module. Hier is hoe je van een
Uint8Array naar Base64 en terug gaat in beide omgevingen:
// --- 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=="Afbeeldingen Inbedden als Data-URI's
Een van de meest praktische toepassingen van Base64 in webontwikkeling is het direct inbedden van afbeeldingen in HTML of CSS, waardoor een HTTP-verzoek wordt geëlimineerd. Je hebt data-URI's waarschijnlijk al gezien in inline SVG's of e-mailtemplates. Hier is het patroon:
<!-- 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-imageEen Compact Hulpmodule voor Beide Omgevingen
In plaats van btoa()-aanroepen door je codebase te verspreiden, is het de moeite
waard om één hulpmodule te hebben die Unicode, URL-safe varianten dekt en werkt in zowel de browser als
Node.js. Hier is er een die dat allemaal doet:
// 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]"Veelvoorkomende Valkuilen om op te Letten
- btoa() gooit een fout bij niet-Latin-tekens — elk teken boven codepunt 255 veroorzaakt
InvalidCharacterError. Gebruik altijd deTextEncoder-aanpak ofBuffer.from(str, 'utf8')in Node.js. - Opvulling is belangrijk voor decodering — Base64-strings moeten een lengte hebben die een veelvoud van 4 is. Ontbrekende
=-opvulling zorgt ervoor datatob()stilletjes rommel retourneert of een fout gooit, afhankelijk van de browser. Herstel altijd de opvulling voordat je URL-safe strings decodeert. - Buffer vs stringcodering in Node.js —
Buffer.from(str)is standaard UTF-8, maarBuffer.from(str, 'binary')behandelt de string als Latin-1-bytes. Het gebruik van de verkeerde codering bij het decoderen produceert onleesbare uitvoer die moeilijk te debuggen kan zijn. - Data-URI MIME-type —
data:;base64,...(geen MIME-type) werkt in sommige browsers maar niet in andere. Voeg altijd het MIME-type toe:data:image/png;base64,.... - Regelafbrekingen in MIME Base64 — RFC 4648 staat implementaties toe regelafbrekingen in te voegen elke 76 tekens (zoals e-mailcoders doen).
atob()enBuffer.from()verwerken dit allebei, maar als je zelf Base64 genereert, voeg dan geen regelafbrekingen toe tenzij het doelsysteem ze verwacht.
Samenvatting
Base64 in JavaScript is een van die onderwerpen die triviaal lijkt totdat het je bijt.
De korte versie: gebruik nooit kale btoa() voor iets door gebruikers gegenereerd — wikkel het
in met TextEncoder om Unicode correct te verwerken. In Node.js is Buffer.from(str,
'utf8').toString('base64') het juiste idioom. Wanneer de gecodeerde string in een URL of JWT
terechtkomt, schakel dan over naar de URL-safe variant. Voor snelle experimenten of eenmalige conversies
besparen de tools Base64 Encoder, Base64 Decoder
en JSON to Base64 tijd. De
MDN Base64-woordenlijstpagina
heeft ook een solide browsergericht naslagwerk als je een tweede mening nodig hebt over een van deze punten.