Base64 in JavaScript: btoa(), Buffer en Unicode

Β·Front-end & Node.js DeveloperΒ·Beoordeeld doorSophie LaurentΒ·Gepubliceerd

Gebruik de gratis Base64 Encode Online direct in je browser β€” geen installatie nodig.

Base64 Encode Online online uitproberen β†’

Wanneer je een afbeelding inbedt in een CSS data URI, inloggegevens doorgeeft in een HTTP Authorization-header of een binair certificaat opslaat in een omgevingsvariabele, moet je Base64-codering betrouwbaar toepassen in JavaScript β€” zowel in de browser als in Node.js. JavaScript biedt twee afzonderlijke ingebouwde API's:btoa() voor browseromgevingen (ook beschikbaar in Node.js 16+) en Buffer.from() voor Node.js β€” elk met verschillende beperkingen rond Unicode, binaire data en URL-veiligheid. Voor snelle eenmalige codering zonder code te schrijven ToolDeck's Base64 Encoder doet het direct in de browser. Deze gids behandelt beide omgevingen met productieklare voorbeelden: Unicode-verwerking, URL-veilige varianten, bestands- en API-responscodering, CLI-gebruik en de vier fouten die consistent bugs veroorzaken in echte codebases.

  • βœ“btoa() is ingebouwd in de browser en beschikbaar in Node.js 16+ als globale functie, maar accepteert alleen Latin-1 (codepunten 0–255) β€” Unicode-invoer gooit een DOMException
  • βœ“Buffer.from(text, "utf8").toString("base64") is het Node.js-equivalent en verwerkt Unicode native zonder extra stappen
  • βœ“URL-veilige Base64 vervangt + β†’ -, / β†’ _ en verwijdert = padding β€” gebruik Buffer.from().toString("base64url") in Node.js 18+ voor een one-liner
  • βœ“Voor binaire data (ArrayBuffer, Uint8Array, bestanden) gebruik je Buffer in Node.js of de arrayBuffer() + Uint8Array-aanpak in de browser β€” nooit response.text()
  • βœ“Uint8Array.prototype.toBase64() (TC39 Stage 3) is al beschikbaar in Node.js 22+ en Chrome 130+ en zal beide omgevingen samenvoegen

Wat is Base64-codering?

Base64 converteert willekeurige binaire data naar een string opgebouwd uit 64 afdrukbare ASCII-tekens: A–Z, a–z, 0–9, + en /. Elke 3 bytes invoer worden precies omgezet naar 4 Base64-tekens; als de invoerlengte geen veelvoud van 3 is, worden één of twee = opvultekens toegevoegd. De gecodeerde uitvoer is altijd ongeveer 33% groter dan het origineel.

Base64 is geenversleuteling β€” het biedt geen vertrouwelijkheid. Iedereen met de gecodeerde string kan deze in één functieaanroep decoderen. Het doel is transportveiligheid: veel protocollen en opslagformaten zijn ontworpen voor 7-bits ASCII-tekst en kunnen geen willekeurige binaire bytes verwerken. Base64 overbrugt dat gat. Veelgebruikte toepassingen in JavaScript zijn: data URI's voor het inlinen van assets, HTTP Basic Auth-headers, JWT-tokensegmenten, e-mail MIME-bijlagen en het opslaan van binaire blobs in JSON API's.

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

btoa() β€” de ingebouwde coderingsfunctie van de browser

btoa() (binary-to-ASCII) is beschikbaar in browsers sinds IE10 en werd een globale functie in Node.js 16.0 als onderdeel van het WinterCG-initiatief. Het werkt ook native in Deno, Bun en Cloudflare Workers. Er is geen import nodig.

De functie neemt één string-argument en retourneert de Base64-gecodeerde vorm ervan. De symmetrische tegenhanger atob() (ASCII-to-binary) decodeert het terug. Beide zijn synchroon en werken met constant geheugen relatief aan de invoergrootte.

Minimaal werkend voorbeeld

JavaScript (browser / Node.js 16+)
// Een API-inloggegevenspaar coderen voor een 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=

Decoderen met atob()

JavaScript
// Heen en terug: coderen, verzenden, decoderen
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
Opmerking:btoa() en atob() maken deel uit van de WinterCG Minimum Common API β€” dezelfde specificatie die Fetch, URL en crypto in niet-browserruntimes regelt. Ze gedragen zich identiek in Node.js 16+, Bun, Deno en Cloudflare Workers.

Omgaan met Unicode en niet-ASCII-tekens

De meest voorkomende valkuil van btoa() is de strikte Latin-1-grens. Elk teken met een codepunt boven U+00FF veroorzaakt onmiddellijk een uitzondering:

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

De juiste aanpak is de string eerst coderen naar UTF-8-bytes en die bytes vervolgens Base64-coderen. JavaScript biedt TextEncoder precies voor dit doel:

TextEncoder-aanpak β€” veilig voor elke Unicode-invoer

JavaScript (browser + Node.js 16+)
// Hulpfuncties voor Unicode-veilige 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)
}

// Werkt met elke taal of elk schrift
const orderNote = 'Bevestigd: Pieter van den Berg β€” magazijn in Rotterdam, aantal: 250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(encoded)
// QmV2ZXN0aWdkOiBQaWV0ZXIgdmFuIGRlbiBCZXJnIOKAkyBtYWdhempuIGluIFJvdHRlcmRhbSwgYWFudGFsOiAyNTA=

console.log(decoded === orderNote) // true
Opmerking:Als je al in Node.js zit, sla de TextEncoder-omweg dan volledig over β€” gebruik Buffer.from(text, 'utf8').toString('base64'). Het verwerkt Unicode native en is sneller voor grote strings.

Buffer.from() in Node.js β€” complete gids met voorbeelden

In Node.js is Buffer de idiomatische API voor alle binaire databewerkingen, inclusief coderingsconversies. Het bestaat jaren voor TextEncoder en blijft de voorkeurskeuze voor server-side code. Belangrijkste voordelen ten opzichte van btoa(): native UTF-8-ondersteuning, verwerking van binaire data en de 'base64url'-coderingssnelkoppeling beschikbaar sinds Node.js 18.

Basis tekst coderen en decoderen

Node.js
// Een serverconfiguratie-object coderen voor opslag in een omgevingsvariabele
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...

// Terug decoderen
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

Binaire bestanden van schijf coderen

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

// Een TLS-certificaat inlezen en coderen voor insluiting in een configuratiebestand
const certPem     = readFileSync(join(process.cwd(), 'ssl', 'server.crt'))
const certBase64  = certPem.toString('base64')

// Opslaan als één regel β€” geschikt voor omgevingsvariabelen of JSON-configuraties
writeFileSync('./dist/cert.b64', certBase64, 'utf8')

console.log(`Certificaat gecodeerd: ${certBase64.length} tekens`)
// Certificaat gecodeerd: 2856 tekens

// Het binaire certificaat herstellen vanuit de gecodeerde waarde
const restored = Buffer.from(certBase64, 'base64')
console.log(restored.equals(certPem)) // true

Asynchroon bestanden coderen met foutafhandeling

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(`Bestand niet gevonden: ${filePath}`)
    if (code === 'EACCES') throw new Error(`Toegang geweigerd: ${filePath}`)
    throw err
  }
}

// Een PDF coderen voor een e-mailbijlage
const reportBase64 = await encodeFileToBase64('./reports/q1-financials.pdf')

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

console.log(`Bijlage: ${reportBase64.length} tekens`)

JavaScript Base64-functies β€” parametersreferentie

In tegenstelling tot de base64-module van Python heeft JavaScript geen enkele uniforme Base64-functie. De API is afhankelijk van de doelomgeving. Hier is de volledige referentie voor alle native benaderingen:

FunctieInvoertypeUnicodeURL-veiligBeschikbaar in
btoa(string)string (Latin-1)❌ throws above U+00FF❌ manual replaceBrowser, Node 16+, Bun, Deno
atob(string)Base64 string❌ returns binary string❌ manual replaceBrowser, Node 16+, Bun, Deno
Buffer.from(src, enc) .toString(enc)string | Buffer | Uint8Arrayβœ… utf8 encodingβœ… base64url in Node 18+Node.js, Bun
TextEncoder().encode(str) + btoa()string (any Unicode)βœ… via UTF-8 bytes❌ manual replaceBrowser, Node 16+, Deno
Uint8Array.toBase64() (TC39)Uint8Arrayβœ… binaryβœ… omitPadding + alphabetChrome 130+, Node 22+

De handtekening Buffer.from(src, enc).toString(enc) accepteert verschillende coderingswaarden die relevant zijn voor Base64:

"base64"
Standaard Base64 (RFC 4648 Β§4). Gebruikt + en / met = opvulling.
"base64url"
URL-veilige Base64 (RFC 4648 Β§5, Node.js 18+). Gebruikt - en _ zonder opvulling.
"utf8"
Standaard voor stringbronnen. Gebruik wanneer de bron leesbare tekst is.
"binary"
Latin-1 / ISO-8859-1. Gebruik wanneer de bron een onbewerkte binaire string is (bijv. van atob()).

URL-veilige Base64 β€” codering voor JWT's, URL's en bestandsnamen

Standaard Base64 gebruikt + en /, die gereserveerd zijn in URL's β€” + wordt in querystrings gedecodeerd als spatie, en /is een padscheidingsteken. JWT's, URL-parameters, bestandsnamen en cookie-waarden vereisen de URL-veilige variant: + β†’ -, / β†’ _, afsluitende = verwijderd.

Browser β€” handmatige tekenvervanging

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'-codering

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

Bestanden en API-reacties coderen in JavaScript

In productiecode wordt Base64-codering het vaakst toegepast op bestanden die worden verzonden en op reacties van externe API's die binaire inhoud leveren. De aanpak verschilt tussen browser en Node.js, en binaire data vereist speciale aandacht.

Browser β€” een bestand coderen vanuit een input-element

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}`

    // Afbeelding inline weergeven
    const img   = document.getElementById('preview') as HTMLImageElement
    img.src     = dataUri
    img.hidden  = false

    console.log(`Gecodeerd ${file.name} (${file.size} bytes) β†’ ${encoded.length} Base64-tekens`)
  } catch (err) {
    console.error('Codering mislukt:', err)
  }
})

Base64-gecodeerde binaire data ophalen van een API

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}`)

Wanneer je alleen een gecodeerde reactie wilt inspecteren tijdens het debuggen van een API zonder een script op te zetten, plak de Base64-waarde dan direct in de Base64 Encoder β€” die decodeert ook, met directe uitvoer. Handig voor het inspecteren van GitHub API-reacties, JWT-payloads en webhook-handtekeningen.

Base64-codering via de opdrachtregel in Node.js en Shell

Voor CI/CD-scripts, Makefile-doelen of eenmalig debuggen heb je zelden een volledig script nodig. Zowel systeemtools als Node.js-one-liners dekken de meeste gevallen platformonafhankelijk.

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=="
Opmerking:Op macOS breekt base64 de uitvoer standaard af na 76 tekens. Dit verstoort downstream parsing. Voeg altijd -b 0 (macOS) of --wrap=0 (Linux) toe wanneer je een enkellijnig resultaat nodig hebt β€” bijvoorbeeld bij het wegschrijven naar een omgevingsvariabele of een configuratieveld.

Hoogwaardige alternatief: js-base64

De ingebouwde API's zijn prima voor de meeste toepassingen. De belangrijkste reden om naar een bibliotheek te grijpen is consistentie over omgevingen: als je een pakket levert dat in zowel browser als Node.js werkt, vereist het gebruik van Buffer omgevingsdetectie of bundelerconfiguratie, terwijl btoa() de Unicode-omweg vereist. js-base64 (100M+ wekelijkse npm-downloads) verwerkt dit transparant.

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

Achter de schermen gebruikt js-base64 native Buffer wanneer beschikbaar en valt terug op een pure-JS implementatie in de browser. Het is 2–3Γ— sneller dan de TextEncoder+btoa-aanpak voor grote Unicode-strings, en de symmetrische API (toBase64 / fromBase64) elimineert de mentale belasting van onthouden in welke richting btoa en atob gaan.

Grote binaire bestanden coderen met Node.js-streams

Wanneer je bestanden groter dan ~50 MB moet coderen, wordt het laden van het volledige bestand in geheugen met readFileSync() een probleem. Node.js-streams laten je data in brokken verwerken β€” maar Base64-codering heeft een beperking: je moet de encoder voeden met veelvouden van 3 bytes om onjuiste opvulling op chunkgrenzen te voorkomen.

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('Streamcodering voltooid')
Opmerking:De chunkgrootte moet een veelvoud van 3 bytes zijn om valse = opvulling in het midden van de uitvoer te voorkomen. Het voorbeeld gebruikt 3 * 1024 * 256 = 786.432 bytes (768 KB) β€” pas highWaterMark aan op basis van je geheugenbudget. Voor bestanden onder 50 MB is readFile() + Buffer.toString('base64') eenvoudiger en snel genoeg.

Veelgemaakte fouten

Ik heb veel JavaScript-codebases met Base64-codering bekeken, en deze vier fouten komen consistent voor β€” vaak onontdekt totdat een niet-ASCII-teken of een binair bestand het coderingspad in productie bereikt.

Fout 1 β€” Unicode direct doorgeven aan btoa()

Probleem: btoa() accepteert alleen tekens met codepunten 0–255. Tekens zoals Γ±, emoji of CJK-ideogrammen veroorzaken een onmiddellijke DOMException. Oplossing: codeer eerst met TextEncoder, of gebruik Buffer.from(text, 'utf8').toString('base64') in Node.js.

Before Β· JavaScript
After Β· JavaScript
// ❌ DOMException: The string to be encoded contains
//    characters outside of the Latin1 range
const username = 'Pieter van den Berg'
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('Pieter van den Berg')
// UGlldGVyIHZhbiBkZW4gQmVyZw==

Fout 2 β€” Vergeten opvulling te herstellen voor atob()

Probleem: URL-veilige Base64 verwijdert de =-opvulling. De gestripte string direct doorgeven aan atob() geeft incorrecte uitvoer of gooit een uitzondering afhankelijk van de stringlengte. Oplossing: herstel + en / en voeg de juiste hoeveelheid opvulling toe voor het aanroepen van atob().

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"}

Fout 3 β€” Gecodeerde chunks samenvoegen in plaats van ruwe buffers

Probleem: Elke aanroep van btoa() of .toString('base64') voegt zijn eigen opvulling toe. Het samenvoegen van twee gevulde Base64-strings geeft ongeldige uitvoer omdat opvulling alleen aan het absolute einde hoort. Oplossing: voeg de ruwe data samen voor het coderen.

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

Fout 4 β€” response.text() gebruiken om binaire API-data te lezen voor codering

Probleem: response.text() interpreteert de ruwe bytes als UTF-8 en vervangt niet-herkende bytesequenties door het vervangingsteken U+FFFD. Alle binaire inhoud β€” afbeeldingen, PDF's, audio β€” wordt stilzwijgend beschadigd voordat het btoa() bereikt. Oplossing: gebruik response.arrayBuffer() voor ruwe bytes.

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 β€” snelle vergelijking

MethodeUnicodeBinaire dataURL-veiligOmgevingenVereist installatie
btoa() / atob()❌ Latin-1❌ workaround needed❌ manual replaceBrowser, Node 16+, Bun, DenoNee
TextEncoder + btoa()βœ… UTF-8βœ… via Uint8Array❌ manual replaceBrowser, Node 16+, DenoNee
Buffer.from().toString()βœ… utf8βœ… nativeβœ… base64url (Node 18+)Node.js, BunNee
Uint8Array.toBase64() (TC39)βœ… binaryβœ… nativeβœ… alphabet optionChrome 130+, Node 22+Nee
js-base64βœ… alwaysβœ… Uint8Arrayβœ… built-inUniversalnpm install

Kies btoa()alleen wanneer de invoer aantoonbaar alleen ASCII bevat β€” hex-digests, numerieke ID's of vooraf gevalideerde Latin-1-strings. Voor door de gebruiker aangeleverde tekst in een browser, gebruik TextEncoder + btoa(). Voor alle Node.js server-side code is Buffer de juiste standaard. Voor bibliotheken die in beide omgevingen moeten werken zonder bundelerconfiguratie verwijdert js-base64 alle randgevallen.

Veelgestelde vragen

Waarom gooit btoa() "InvalidCharacterError" voor mijn string?
btoa() accepteert alleen tekens met codepunten in het bereik 0–255 (Latin-1 / ISO-8859-1). Elk teken boven U+00FF β€” inclusief de meeste Cyrillische, Arabische, CJK-ideogrammen en veel emoji β€” veroorzaakt een DOMException. De oplossing hangt af van je omgeving: in de browser codeer je eerst naar UTF-8-bytes met TextEncoder, converteer je elke byte naar een teken met String.fromCharCode(), en roep je dan btoa() aan. In Node.js gebruik je Buffer.from(text, 'utf8').toString('base64') dat Unicode native verwerkt.
Is btoa() beschikbaar in Node.js zonder enige import?
Ja, since Node.js 16.0. Beide btoa() en atob() zijn geregistreerd als globale functies β€” geen import nodig. Ze gedragen zich identiek aan hun browsercounterparts, inclusief de Latin-1-beperking. Voor Node.js-servercode heeft Buffer.from() nog steeds de voorkeur boven btoa() omdat het UTF-8 native verwerkt, binaire data ondersteunt zonder omwegen, en de 'base64url'-coderingsoptie heeft toegevoegd in Node.js 18.
Wat is het verschil tussen standaard Base64 en URL-veilige Base64?
Standaard Base64 (RFC 4648 Β§4) gebruikt + voor waarde 62, / voor waarde 63 en = voor opvulling. Deze tekens hebben speciale betekenis in URL's: + wordt geΓ―nterpreteerd als spatie in querystrings, en / is een padscheidingsteken. URL-veilige Base64 (RFC 4648 Β§5) vervangt - voor + en _ voor /, en laat de = opvulling typisch volledig weg. JWT's gebruiken URL-veilige Base64 voor alle drie segmenten. In Node.js 18+ produceert Buffer.from(text).toString('base64url') het URL-veilige formaat direct.
Hoe codeer ik een afbeelding naar Base64 voor een CSS data URI in JavaScript?
In een browser: gebruik file.arrayBuffer() om de binaire data te lezen, converteer naar Uint8Array, roep dan btoa(Array.from(bytes, b => String.fromCharCode(b)).join('')) aan. Bouw de data URI op als 'data:' + file.type + ';base64,' + encoded. In Node.js: const encoded = fs.readFileSync('./image.png').toString('base64') en voeg het MIME-type toe. Voor SVG-bestanden kun je Base64 vaak volledig overslaan en een URL-gecodeerde data URI gebruiken β€” dat is leesbaarder en iets kleiner.
Kan ik Base64 coderen en decoderen zonder een npm-bibliotheek in de browser?
Ja. Voor ASCII-invoer werken btoa() en atob() direct. Voor Unicode geeft het TextEncoder / TextDecoder-paar je de volledige gereedschapskist β€” beide zijn ingebouwd in alle moderne browsers en Node.js 16+. Het enige geval waarbij een bibliotheek echt waarde toevoegt is consistentie over omgevingen: als je een utility schrijft die identiek moet werken in zowel browser als Node.js zonder bundelerconfiguratie, verwijdert js-base64 de omgevingsdetectielogica.
Hoe decodeer ik Base64-inhoud van de GitHub API?
De GitHub Contents API retourneert bestandsinhoud als Base64 met ingebedde newline-tekens (de API breekt uitvoer af na 60 tekens). Verwijder ze voor het decoderen: 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 gebruikt altijd standaard Base64 (niet URL-veilig), dus geen + β†’ - of / β†’ _ vervanging nodig.

Gerelateerde tools

Voor eenkliks coderen of decoderen zonder code te schrijven, plak je string of binaire data direct in de Base64 Encoder β€” het verwerkt standaard en URL-veilige modi direct in je browser.

Ook beschikbaar 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 LaurentTechnisch beoordelaar

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.