Base64 in JavaScript kodieren — btoa() & Buffer

·Front-end & Node.js Developer·Geprüft vonSophie Laurent·Veröffentlicht

Nutze das kostenlose Base64 Encode Online direkt im Browser – keine Installation erforderlich.

Base64 Encode Online online testen →

Wenn du ein Bild in eine CSS-Daten-URI einbettest, Anmeldedaten in einem HTTP-Authorization-Header übermittelst oder ein binäres Zertifikat in einer Umgebungsvariablen speicherst, musst du JavaScript-Daten zuverlässig sowohl im Browser als auch in Node.js in Base64 kodieren. JavaScript bietet zwei verschiedene integrierte APIs:btoa() für Browser-Umgebungen (auch in Node.js 16+ verfügbar) und Buffer.from() für Node.js — jede mit unterschiedlichen Einschränkungen bei Unicode, Binärdaten und URL-Sicherheit. Für eine schnelle Einmal-Kodierung ohne Code zu schreiben, erledigt ToolDecks Base64-Encoder dies sofort im Browser. Dieser Leitfaden behandelt beide Umgebungen mit produktionsreifen Beispielen: Unicode-Behandlung, URL-safe-Varianten, Datei- und API-Antwort-Kodierung, CLI-Nutzung und die vier Fehler, die in realen Codebasen regelmäßig Bugs verursachen.

  • btoa() ist browsernativ und in Node.js 16+ global verfügbar, akzeptiert jedoch nur Latin-1 (Codepunkte 0–255) — Unicode-Eingaben werfen eine DOMException
  • Buffer.from(text, "utf8").toString("base64") ist das Node.js-Äquivalent und verarbeitet Unicode nativ ohne zusätzliche Schritte
  • URL-safe Base64 ersetzt + → -, / → _, und entfernt das =-Padding — nutze Buffer.from().toString("base64url") in Node.js 18+ als Einzeiler
  • Für Binärdaten (ArrayBuffer, Uint8Array, Dateien) verwende Buffer in Node.js oder den arrayBuffer() + Uint8Array-Ansatz im Browser — niemals response.text()
  • Uint8Array.prototype.toBase64() (TC39 Stage 3) ist bereits in Node.js 22+ und Chrome 130+ verfügbar und wird beide Umgebungen vereinheitlichen

Was ist Base64-Kodierung?

Base64 wandelt beliebige Binärdaten in einen String aus 64 druckbaren ASCII-Zeichen um: A–Z, a–z, 0–9, + und /. Je 3 Eingabebytes werden auf genau 4 Base64-Zeichen abgebildet; wenn die Eingabelänge kein Vielfaches von 3 ist, werden ein oder zwei =-Füllzeichen angehängt. Die kodierte Ausgabe ist immer etwa 33% größer als das Original.

Base64 ist keine Verschlüsselung — es bietet keine Vertraulichkeit. Jeder mit dem kodierten String kann ihn mit einem einzigen Funktionsaufruf dekodieren. Sein Zweck ist Transportsicherheit: Viele Protokolle und Speicherformate wurden für 7-Bit-ASCII-Text entwickelt und können keine beliebigen Binärbytes verarbeiten. Base64 überbrückt diese Lücke. Häufige JavaScript-Anwendungsfälle umfassen Daten-URIs zum Einbetten von Assets, HTTP Basic Auth-Header, JWT-Token-Segmente, E-Mail-MIME-Anhänge und die Speicherung von Binär-Blobs in JSON-APIs.

Before · text
After · text
deploy-bot:sk-prod-a7f2c91e4b3d8
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=

btoa() — Die browsernative Kodierungsfunktion

btoa() (binary-to-ASCII) ist seit IE10 in Browsern verfügbar und wurde in Node.js 16.0 als Teil der WinterCG-Kompatibilitätsinitiative zu einer globalen Funktion. Sie funktioniert auch nativ in Deno, Bun und Cloudflare Workers. Kein Import ist erforderlich.

Die Funktion nimmt ein einzelnes String-Argument und gibt seine Base64-kodierte Form zurück. Das symmetrische Gegenstück atob() (ASCII-to-binary) dekodiert es wieder zurück. Beide sind synchron und laufen mit konstantem Speicher relativ zur Eingabegröße.

Minimales funktionierendes Beispiel

JavaScript (browser / Node.js 16+)
// Encoding an API credential pair for an HTTP Basic Auth header
const serviceId  = 'deploy-bot'
const apiKey     = 'sk-prod-a7f2c91e4b3d8'

const credential = btoa(`${serviceId}:${apiKey}`)
// → 'ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg='

const headers = new Headers({
  Authorization: `Basic ${credential}`,
  'Content-Type': 'application/json',
})

console.log(headers.get('Authorization'))
// Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=

Dekodierung mit atob()

JavaScript
// Round-trip: encode, transmit, decode
const payload = 'service:payments region:eu-west-1 env:production'

const encoded = btoa(payload)
const decoded = atob(encoded)

console.log(encoded)
// c2VydmljZTpwYXltZW50cyByZWdpb246ZXUtd2VzdC0xIGVudjpwcm9kdWN0aW9u

console.log(decoded === payload) // true
Hinweis:btoa() und atob() sind Teil der WinterCG Minimum Common API — derselben Spezifikation, die Fetch, URL und crypto in Nicht-Browser-Runtimes regelt. Sie verhalten sich identisch in Node.js 16+, Bun, Deno und Cloudflare Workers.

Umgang mit Unicode und Nicht-ASCII-Zeichen

Die häufigste Falle bei btoa() ist die strikte Latin-1-Grenze. Jedes Zeichen mit einem Codepunkt über U+00FF verursacht sofort eine Ausnahme:

JavaScript
btoa('Müller & Søren') // ❌ Uncaught DOMException: String contains an invalid character
btoa('résumé')         // ❌ 'é' is U+00E9 = 233 — within Latin-1, this one actually works
btoa('田中太郎')         // ❌ Throws — all CJK characters are above U+00FF

Der richtige Ansatz besteht darin, den String zuerst in UTF-8-Bytes zu kodieren und dann diese Bytes in Base64 zu kodieren. JavaScript stellt TextEncoder genau für diesen Zweck bereit:

TextEncoder-Ansatz — sicher für beliebige Unicode-Eingaben

JavaScript (browser + Node.js 16+)
// Utility functions for Unicode-safe Base64
function toBase64(text: string): string {
  const bytes = new TextEncoder().encode(text)
  const chars = Array.from(bytes, byte => String.fromCharCode(byte))
  return btoa(chars.join(''))
}

function fromBase64(encoded: string): string {
  const binary = atob(encoded)
  const bytes  = Uint8Array.from(binary, ch => ch.charCodeAt(0))
  return new TextDecoder().decode(bytes)
}

// Works with any language or script
const orderNote = 'Bestätigt: Lukas Bauer — Lager Berlin, Menge: 250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(encoded)
// QmVzdMOkdGlndDogTHVrYXMgQmF1ZXIg4oCTIExhZ2VyIEJlcmxpbiwgTWVuZ2U6IDI1MA==

console.log(decoded === orderNote) // true
Hinweis:Wenn du bereits in Node.js bist, überspringe den TextEncoder-Workaround vollständig — verwende Buffer.from(text, 'utf8').toString('base64'). Es verarbeitet Unicode nativ und ist bei großen Strings schneller.

Buffer.from() in Node.js — Vollständige Anleitung mit Beispielen

In Node.js ist Buffer die idiomatische API für alle Binärdatenoperationen, einschließlich Kodierungskonvertierungen. Es ist TextEncoder um Jahre voraus und bleibt die bevorzugte Wahl für serverseitigen Code. Wesentliche Vorteile gegenüber btoa(): nativer UTF-8-Support, Binärdatenverarbeitung und die 'base64url'-Kodierungsabkürzung, die seit Node.js 18 verfügbar ist.

Grundlegende Text-Kodierung und -Dekodierung

Node.js
// Encoding a server configuration object for storage in an env variable
const dbConfig = JSON.stringify({
  host:           'db-primary.internal',
  port:           5432,
  database:       'analytics_prod',
  maxConnections: 100,
  ssl:            { rejectUnauthorized: true },
})

const encoded = Buffer.from(dbConfig, 'utf8').toString('base64')
console.log(encoded)
// eyJob3N0IjoiZGItcHJpbWFyeS5pbnRlcm5hbCIsInBvcnQiOjU0MzIsImRhdGFiYXNlIjoiYW5h...

// Decoding back
const decoded = Buffer.from(encoded, 'base64').toString('utf8')
const config  = JSON.parse(decoded)

console.log(config.host)           // db-primary.internal
console.log(config.maxConnections) // 100

Binärdateien von der Festplatte kodieren

Node.js
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'

// Read a TLS certificate and encode it for embedding in a config file
const certPem     = readFileSync(join(process.cwd(), 'ssl', 'server.crt'))
const certBase64  = certPem.toString('base64')

// Store as a single-line string — suitable for env vars or JSON configs
writeFileSync('./dist/cert.b64', certBase64, 'utf8')

console.log(`Certificate encoded: ${certBase64.length} characters`)
// Certificate encoded: 2856 characters

// Restore the binary cert from the encoded value
const restored = Buffer.from(certBase64, 'base64')
console.log(restored.equals(certPem)) // true

Asynchrone Datei-Kodierung mit Fehlerbehandlung

Node.js
import { readFile } from 'node:fs/promises'

async function encodeFileToBase64(filePath: string): Promise<string> {
  try {
    const buffer = await readFile(filePath)
    return buffer.toString('base64')
  } catch (err) {
    const code = (err as NodeJS.ErrnoException).code
    if (code === 'ENOENT') throw new Error(`File not found: ${filePath}`)
    if (code === 'EACCES') throw new Error(`Permission denied: ${filePath}`)
    throw err
  }
}

// Encode a PDF for an email attachment payload
const reportBase64 = await encodeFileToBase64('./reports/q1-financials.pdf')

const emailPayload = {
  to:          'finance-team@company.internal',
  subject:     'Q1 Financial Report',
  attachments: [{
    filename:    'q1-financials.pdf',
    content:     reportBase64,
    encoding:    'base64',
    contentType: 'application/pdf',
  }],
}

console.log(`Attachment: ${reportBase64.length} chars`)

JavaScript Base64-Funktionen — Parameterreferenz

Im Gegensatz zu Pythons base64-Modul hat JavaScript keine einzige einheitliche Base64-Funktion. Die API hängt von der Zielumgebung ab. Hier ist die vollständige Referenz für alle nativen Ansätze:

FunktionEingabetypUnicodeURL-safeVerfügbar in
btoa(string)string (Latin-1)❌ wirft über U+00FF❌ manuelles ErsetzenBrowser, Node 16+, Bun, Deno
atob(string)Base64 string❌ gibt binären String zurück❌ manuelles ErsetzenBrowser, Node 16+, Bun, Deno
Buffer.from(src, enc) .toString(enc)string | Buffer | Uint8Array✅ utf8-Kodierung✅ base64url in Node 18+Node.js, Bun
TextEncoder().encode(str) + btoa()string (beliebiges Unicode)✅ über UTF-8-Bytes❌ manuelles ErsetzenBrowser, Node 16+, Deno
Uint8Array.toBase64() (TC39)Uint8Array✅ binär✅ omitPadding + alphabetChrome 130+, Node 22+

Die Signatur Buffer.from(src, enc).toString(enc) akzeptiert mehrere für Base64 relevante Kodierungswerte:

"base64"
Standard-Base64 (RFC 4648 §4). Verwendet + und / mit =-Padding.
"base64url"
URL-safe Base64 (RFC 4648 §5, Node.js 18+). Verwendet - und _ ohne Padding.
"utf8"
Standard für String-Quellen. Verwenden, wenn die Quelle menschenlesbarer Text ist.
"binary"
Latin-1 / ISO-8859-1. Wird verwendet, wenn die Quelle ein roher binärer String ist (z. B. von atob()).

URL-safe Base64 — Kodierung für JWTs, URLs und Dateinamen

Standard-Base64 verwendet + und /, die in URLs reserviert sind — + wird in Query-Strings als Leerzeichen dekodiert, und / ist ein Pfadtrennzeichen. JWTs, URL-Parameter, Dateinamen und Cookie-Werte erfordern alle die URL-safe-Variante: +-, /_, abschließendes = entfernt.

Browser — manuelles Zeichenersetzen

JavaScript (browser)
function toBase64Url(text: string): string {
  // For ASCII-safe input (e.g., JSON with only ASCII chars)
  return btoa(text)
    .replace(/+/g, '-')
    .replace(///g, '_')
    .replace(/=/g, '')
}

function fromBase64Url(encoded: string): string {
  // Restore standard Base64 characters and padding before decoding
  const base64  = encoded.replace(/-/g, '+').replace(/_/g, '/')
  const padded  = base64 + '==='.slice(0, (4 - base64.length % 4) % 4)
  return atob(padded)
}

// JWT header — must be URL-safe Base64
const header  = JSON.stringify({ alg: 'HS256', typ: 'JWT' })
const encoded = toBase64Url(header)
console.log(encoded) // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

const decoded = fromBase64Url(encoded)
console.log(decoded) // {"alg":"HS256","typ":"JWT"}

Node.js 18+ — native 'base64url'-Kodierung

Node.js 18+
// Node.js 18 added 'base64url' as a first-class Buffer encoding
const sessionPayload = JSON.stringify({
  userId:     'usr_9f2a1c3e8b4d',
  role:       'editor',
  workspaceId:'ws_3a7f91c2',
  exp:        Math.floor(Date.now() / 1000) + 3600,
})

const encoded = Buffer.from(sessionPayload, 'utf8').toString('base64url')
// No + or / or = characters in the output
// eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9

const decoded = Buffer.from(encoded, 'base64url').toString('utf8')
console.log(JSON.parse(decoded).role) // editor

Dateien und API-Antworten in JavaScript kodieren

Im Produktionscode wird Base64-Kodierung am häufigsten auf Dateien angewendet, die übertragen werden, und auf Antworten von externen APIs, die Binärinhalte liefern. Die Muster unterscheiden sich zwischen Browser und Node.js, und Binärdaten erfordern besondere Sorgfalt.

Browser — eine Datei aus einem Input-Element kodieren

JavaScript (browser)
// Modern approach: File.arrayBuffer() (Chrome 76+, Firefox 69+, Safari 14+)
async function encodeFile(file: File): Promise<string> {
  const buffer = await file.arrayBuffer()
  const bytes  = new Uint8Array(buffer)
  const chars  = Array.from(bytes, b => String.fromCharCode(b))
  return btoa(chars.join(''))
}

const uploadInput = document.getElementById('avatar') as HTMLInputElement

uploadInput.addEventListener('change', async (e) => {
  const file = (e.target as HTMLInputElement).files?.[0]
  if (!file) return

  try {
    const encoded = await encodeFile(file)
    const dataUri = `data:${file.type};base64,${encoded}`

    // Preview the image inline
    const img   = document.getElementById('preview') as HTMLImageElement
    img.src     = dataUri
    img.hidden  = false

    console.log(`Encoded ${file.name} (${file.size} bytes) → ${encoded.length} Base64 chars`)
  } catch (err) {
    console.error('Encoding failed:', err)
  }
})

Base64-kodierte Binärdaten von einer API abrufen

JavaScript
// GitHub Contents API returns file content as Base64 with embedded newlines
async function fetchRepoFile(
  owner: string,
  repo:  string,
  path:  string,
  token: string,
): Promise<string> {
  const res = await fetch(
    `https://api.github.com/repos/${owner}/${repo}/contents/${path}`,
    {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: 'application/vnd.github.v3+json',
      },
    }
  )

  if (!res.ok) throw new Error(`GitHub API ${res.status}: ${res.statusText}`)

  const data = await res.json() as { content: string; encoding: string; size: number }

  if (data.encoding !== 'base64') {
    throw new Error(`Unexpected encoding from GitHub: ${data.encoding}`)
  }

  // GitHub wraps output at 60 chars — strip newlines before decoding
  const clean = data.content.replace(/\n/g, '')
  return atob(clean)
}

const openApiSpec = await fetchRepoFile(
  'acme-corp', 'platform-api', 'openapi.json', process.env.GITHUB_TOKEN!
)
const spec = JSON.parse(openApiSpec)
console.log(`API version: ${spec.info.version}`)

Wenn du beim API-Debugging eine kodierte Antwort schnell prüfen möchtest, ohne ein Skript einzurichten, füge den Base64-Wert direkt in den Base64-Encoder ein — er dekodiert ebenfalls mit sofortiger Ausgabe. Nützlich für das Prüfen von GitHub-API-Antworten, JWT-Payloads und Webhook-Signaturen.

Base64-Kodierung in der Kommandozeile mit Node.js und Shell

Für CI/CD-Skripte, Makefile-Targets oder einmalige Fehlersuche brauchst du selten ein vollständiges Skript. Sowohl Systemwerkzeuge als auch Node.js-Einzeiler decken die meisten Fälle plattformübergreifend ab.

bash
# ── macOS / Linux system base64 ───────────────────────────────────────
# Standard encoding
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64
# ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=

# URL-safe variant (replace chars and strip padding)
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64 | tr '+/' '-_' | tr -d '='

# Encode a file inline (macOS: -b 0 removes line wrapping; Linux: --wrap=0)
base64 -b 0 ./config/production.json
# or on Linux:
base64 --wrap=0 ./config/production.json

# Decode
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 --decode

# ── Node.js one-liner — works on Windows too ───────────────────────────
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64'))" "my:secret"
# bXk6c2VjcmV0

# URL-safe from Node.js 18+
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64url'))" "my:secret"
# bXk6c2VjcmV0  (same here since there are no special chars)

# Decode in Node.js
node -e "console.log(Buffer.from(process.argv[1], 'base64').toString())" "ZGVwbG95LWJvdA=="
Hinweis:Unter macOS bricht base64 die Ausgabe standardmäßig bei 76 Zeichen um. Das bricht nachgelagerte Parser. Füge immer -b 0 (macOS) oder --wrap=0 (Linux) hinzu, wenn du ein einzeiliges Ergebnis benötigst — zum Beispiel beim Schreiben in eine Umgebungsvariable oder ein Konfigurationsfeld.

Hochperformante Alternative: js-base64

Die integrierten APIs reichen für die meisten Anwendungsfälle aus. Der Hauptgrund für eine Bibliothek ist umgebungsübergreifende Konsistenz: Wenn du ein Paket veröffentlichst, das sowohl im Browser als auch in Node.js läuft, erfordert die Verwendung von Buffer entweder Umgebungserkennung oder Bundler-Konfiguration, während btoa() den Unicode-Workaround benötigt. js-base64 (über 100 Mio. wöchentliche npm-Downloads) behandelt beides transparent.

bash
npm install js-base64
# or
pnpm add js-base64
JavaScript
import { toBase64, fromBase64, toBase64Url, fromBase64Url, isValid } from 'js-base64'

// Standard encoding — Unicode-safe, works in browser and Node.js
const telemetryEvent = JSON.stringify({
  eventId:   'evt_7c3a9f1b2d',
  type:      'checkout_completed',
  currency:  'EUR',
  amount:    14900,
  userId:    'usr_4e2b8d6a5c',
  timestamp: 1717200000,
})

const encoded    = toBase64(telemetryEvent)
const urlEncoded = toBase64Url(telemetryEvent) // No +, /, or = characters

const decoded = fromBase64(encoded)
console.log(JSON.parse(decoded).type) // checkout_completed

// Binary data — pass a Uint8Array as second argument
const pngMagicBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
const binaryEncoded = toBase64(pngMagicBytes, true) // true = binary mode

// Validation before decoding
const suspicious = 'not!valid@base64#'
console.log(isValid(suspicious)) // false

Intern verwendet js-base64 nativen Buffer, wenn verfügbar, und fällt im Browser auf eine reine JS-Implementierung zurück. Es ist 2–3× schneller als der TextEncoder+btoa-Ansatz für große Unicode-Strings, und die symmetrische API (toBase64 / fromBase64) eliminiert den mentalen Aufwand, sich zu merken, in welche Richtung btoa und atob gehen.

Große Binärdateien mit Node.js-Streams kodieren

Wenn du Dateien größer als ~50 MB kodieren musst, wird das Laden der gesamten Datei in den Speicher mit readFileSync() problematisch. Node.js-Streams ermöglichen die Verarbeitung der Daten in Chunks — aber die Base64-Kodierung hat eine Einschränkung: Du musst den Kodierer in Vielfachen von 3 Bytes füttern, um falsches Padding an Chunk-Grenzen zu vermeiden.

Node.js
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'

// Stream a large binary file to a Base64-encoded output file
async function streamEncodeToBase64(
  inputPath:  string,
  outputPath: string,
): Promise<void> {
  const readStream  = createReadStream(inputPath, { highWaterMark: 3 * 1024 * 256 }) // 768 KB chunks (multiple of 3)
  const writeStream = createWriteStream(outputPath, { encoding: 'utf8' })

  let buffer = Buffer.alloc(0)

  await pipeline(
    readStream,
    async function* (source) {
      for await (const chunk of source) {
        buffer = Buffer.concat([buffer, chunk as Buffer])

        // Encode in complete 3-byte groups to avoid mid-stream padding
        const remainder = buffer.length % 3
        const safe      = buffer.subarray(0, buffer.length - remainder)
        buffer          = buffer.subarray(buffer.length - remainder)

        if (safe.length > 0) yield safe.toString('base64')
      }
      // Flush remaining bytes (may add 1 or 2 '=' padding chars)
      if (buffer.length > 0) yield buffer.toString('base64')
    },
    writeStream,
  )
}

// Usage: encode a 200 MB video attachment
await streamEncodeToBase64(
  './uploads/product-demo.mp4',
  './dist/product-demo.b64',
)
console.log('Stream encoding complete')
Hinweis:Die Chunk-Größe muss ein Vielfaches von 3 Bytes sein, um falsches =-Padding in der Mitte der Ausgabe zu vermeiden. Das Beispiel verwendet 3 * 1024 * 256 = 786.432 Bytes (768 KB) — passe highWaterMark deinem Speicherbudget an. Für Dateien unter 50 MB ist readFile() + Buffer.toString('base64') einfacher und schnell genug.

Häufige Fehler

Ich habe viele JavaScript-Codebasen mit Base64-Kodierung überprüft, und diese vier Fehler tauchen regelmäßig auf — oft unentdeckt, bis ein Nicht-ASCII-Zeichen oder eine Binärdatei den Kodierungspfad in der Produktion erreicht.

Fehler 1 — Unicode direkt an btoa() übergeben

Problem: btoa() akzeptiert nur Zeichen mit Codepunkten 0–255. Zeichen wie ñ, Emojis oder CJK-Ideogramme verursachen sofort eine DOMException. Lösung: zuerst mit TextEncoder kodieren, oder Buffer.from(text, 'utf8').toString('base64') in Node.js verwenden.

Before · JavaScript
After · JavaScript
// ❌ DOMException: The string to be encoded contains
//    characters outside of the Latin1 range
const username = 'Sophie Müller'
const encoded  = btoa(username)  // throws
// ✅ Encode as UTF-8 bytes first
function safeEncode(text: string): string {
  const bytes = new TextEncoder().encode(text)
  const chars = Array.from(bytes, b => String.fromCharCode(b))
  return btoa(chars.join(''))
}
const encoded = safeEncode('Sophie Müller')
// U29waGllIE3DvGxsZXI=

Fehler 2 — Vergessen, das Padding vor atob() wiederherzustellen

Problem: URL-safe Base64 entfernt das =-Padding. Den getrimmten String direkt an atob() zu übergeben produziert falsche Ausgabe oder wirft je nach Stringlänge eine Ausnahme. Lösung: + und / wiederherstellen und die richtige Menge Padding vor dem Aufruf von atob() wieder hinzufügen.

Before · JavaScript
After · JavaScript
// ❌ atob() may return wrong data or throw
//    on URL-safe Base64 without padding
const jwtSegment = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ'
const decoded    = atob(jwtSegment) // Unreliable
// ✅ Restore characters and padding first
function decodeBase64Url(input: string): string {
  const b64 = input.replace(/-/g, '+').replace(/_/g, '/')
  const pad = b64 + '==='.slice(0, (4 - b64.length % 4) % 4)
  return atob(pad)
}
const decoded = decodeBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ')
// {"userId":"usr_9f2a1c3e"}

Fehler 3 — Kodierte Chunks statt roher Buffer verketten

Problem: Jeder Aufruf von btoa() oder .toString('base64') fügt sein eigenes Padding hinzu. Das Verketten zweier gepadded Base64-Strings erzeugt ungültige Ausgabe, weil Padding nur ganz am Ende stehen darf. Lösung: die Rohdaten vor der Kodierung verketten.

Before · JavaScript
After · JavaScript
// ❌ Both parts are padded independently —
//    the combined string is not valid Base64
const part1 = Buffer.from('webhook-secret').toString('base64')
// d2ViaG9vay1zZWNyZXQ=  ← has padding
const part2 = Buffer.from('-v2').toString('base64')
// LXYy            ← correct in isolation
const combined = part1 + part2 // ❌ Invalid — padding in the middle
// ✅ Concatenate raw Buffers before encoding
const combined = Buffer.concat([
  Buffer.from('webhook-secret'),
  Buffer.from('-v2'),
]).toString('base64')
// d2ViaG9vay1zZWNyZXQtdjI= — single valid Base64 string

Fehler 4 — response.text() zum Lesen binärer API-Daten vor der Kodierung verwenden

Problem: response.text() interpretiert die Rohbytes als UTF-8 und ersetzt nicht erkannte Byte-Sequenzen durch das Ersetzungszeichen U+FFFD. Jeder Binärinhalt — Bilder, PDFs, Audio — wird still beschädigt, bevor er btoa() erreicht. Lösung: response.arrayBuffer() verwenden, um Rohbytes zu erhalten.

Before · JavaScript
After · JavaScript
// ❌ response.text() corrupts binary data
const res     = await fetch('/api/exports/invoice.pdf')
const text    = await res.text()   // ❌ PDF bytes mangled as UTF-8
const encoded = btoa(text)         // ❌ Corrupted Base64
// ✅ arrayBuffer() preserves raw bytes
const res     = await fetch('/api/exports/invoice.pdf')
const buffer  = await res.arrayBuffer()
const bytes   = new Uint8Array(buffer)
const chars   = Array.from(bytes, b => String.fromCharCode(b))
const encoded = btoa(chars.join('')) // ✅ Valid Base64

JavaScript Base64-Methoden — Schnellvergleich

MethodeUnicodeBinärdatenURL-safeUmgebungenInstallation nötig
btoa() / atob()❌ Latin-1❌ Workaround nötig❌ manuelles ErsetzenBrowser, Node 16+, Bun, DenoNein
TextEncoder + btoa()✅ UTF-8✅ über Uint8Array❌ manuelles ErsetzenBrowser, Node 16+, DenoNein
Buffer.from().toString()✅ utf8✅ nativ✅ base64url (Node 18+)Node.js, BunNein
Uint8Array.toBase64() (TC39)✅ binär✅ nativ✅ alphabet-OptionChrome 130+, Node 22+Nein
js-base64✅ immer✅ Uint8Array✅ eingebautUniversalnpm install

Wähle btoa() nur, wenn die Eingabe nachweislich ausschließlich ASCII ist — Hex-Digests, numerische IDs oder vorvalidierte Latin-1-Strings. Für benutzerbeigestellten Text im Browser verwende TextEncoder + btoa(). Für den gesamten serverseitigen Node.js-Code ist Buffer die richtige Standardwahl. Für Bibliotheken, die in beiden Umgebungen ohne Bundler-Konfiguration laufen müssen, beseitigt js-base64 alle Grenzfälle.

Häufig gestellte Fragen

Warum wirft btoa() "InvalidCharacterError" bei meinem String?
btoa() akzeptiert nur Zeichen mit Codepunkten im Bereich 0–255 (Latin-1 / ISO-8859-1). Jedes Zeichen über U+00FF — einschließlich der meisten kyrillischen, arabischen, CJK-Ideogramm- und vieler Emoji-Zeichen — verursacht eine DOMException. Die Lösung hängt von deiner Umgebung ab: Im Browser zuerst mit TextEncoder in UTF-8-Bytes kodieren, jeden Byte mit String.fromCharCode() in ein Zeichen umwandeln, dann btoa() aufrufen. In Node.js Buffer.from(text, 'utf8').toString('base64') verwenden, das Unicode nativ verarbeitet.
Ist btoa() in Node.js ohne Import verfügbar?
Ja, seit Node.js 16.0. Sowohl btoa() als auch atob() sind als globale Funktionen registriert — kein Import erforderlich. Sie verhalten sich identisch zu ihren Browser-Gegenstücken, einschließlich der Latin-1-Einschränkung. Für serverseitigen Node.js-Code wird Buffer.from() gegenüber btoa() bevorzugt, weil es UTF-8 nativ verarbeitet, Binärdaten ohne Workarounds unterstützt und die 'base64url'-Kodierungsoption hat, die in Node.js 18 hinzugefügt wurde.
Was ist der Unterschied zwischen Standard-Base64 und URL-safe Base64?
Standard-Base64 (RFC 4648 §4) verwendet + für Wert 62, / für Wert 63 und = für Padding. Diese Zeichen haben in URLs besondere Bedeutung: + wird in Query-Strings als Leerzeichen interpretiert, und / ist ein Pfadtrennzeichen. URL-safe Base64 (RFC 4648 §5) ersetzt - durch + und _ durch /, und lässt das =-Padding typischerweise vollständig weg. JWTs verwenden URL-safe Base64 für alle drei Segmente. In Node.js 18+ erzeugt Buffer.from(text).toString('base64url') das URL-safe-Format direkt.
Wie kodiere ich ein Bild in Base64 für eine CSS-Daten-URI in JavaScript?
Im Browser: file.arrayBuffer() zum Lesen des Binärinhalts verwenden, in Uint8Array konvertieren, dann btoa(Array.from(bytes, b => String.fromCharCode(b)).join('')) aufrufen. Die Daten-URI als 'data:' + file.type + ';base64,' + encoded aufbauen. In Node.js: const encoded = fs.readFileSync('./image.png').toString('base64') und den MIME-Typ voranstellen. Für SVG-Dateien kann Base64 oft vollständig übersprungen werden und stattdessen eine URL-kodierte Daten-URI verwendet werden, die lesbarer und etwas kleiner ist.
Kann ich ohne npm-Bibliothek im Browser Base64 kodieren und dekodieren?
Ja. Für reine ASCII-Eingaben funktionieren btoa() und atob() direkt. Für Unicode bietet das Paar TextEncoder / TextDecoder das vollständige Werkzeugset — beide sind in alle modernen Browser und Node.js 16+ integriert. Der einzige Fall, in dem eine Bibliothek echten Mehrwert bietet, ist die umgebungsübergreifende Konsistenz: Wenn du ein Hilfsprogramm schreibst, das in Browser und Node.js ohne Bundler-Konfiguration identisch funktionieren muss, entfernt js-base64 die Umgebungserkennungslogik.
Wie dekodiere ich Base64-Inhalte von der GitHub-API?
Die GitHub Contents API gibt den Dateiinhalt als Base64 mit eingebetteten Zeilenumbrüchen zurück (die API bricht die Ausgabe bei 60 Zeichen um). Diese vor dem Dekodieren entfernen: const clean = data.content.replace(/\n/g, ''); const text = atob(clean);. In Node.js: const text = Buffer.from(data.content.replace(/\n/g, ''), 'base64').toString('utf8');. GitHub verwendet immer Standard-Base64 (nicht URL-safe), daher ist keine + → - oder / → _ Substitution nötig.

Verwandte Tools

Für ein Ein-Klick-Kodieren oder -Dekodieren ohne Code zu schreiben, füge deinen String oder deine Binärdaten direkt in den Base64-Encoder ein — er verarbeitet Standard- und URL-safe-Modi sofort in deinem Browser.

Auch verfügbar in:PythonJava
AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

SL
Sophie LaurentTechnischer Prüfer

Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.