Codificação Base64 em JavaScript: Guia Completo

·Front-end & Node.js Developer·Revisado porSophie Laurent·Publicado

Use o Codificador Base64 Online gratuito diretamente no seu navegador — sem instalação.

Experimentar Codificador Base64 Online online →

Quando você incorpora uma imagem em uma URI de dados CSS, passa credenciais em um cabeçalho HTTP Authorization, ou armazena um certificado binário em uma variável de ambiente, você precisa codificar dados JavaScript em Base64 de forma confiável tanto no navegador quanto no Node.js. O JavaScript fornece duas APIs integradas distintas:btoa() para ambientes de navegador (também disponível no Node.js 16+) e Buffer.from() para Node.js — cada uma com restrições diferentes em torno de Unicode, dados binários e segurança em URLs. Para uma codificação rápida sem escrever nenhum código, o Codificador Base64 do ToolDeck faz isso instantaneamente no navegador. Este guia cobre ambos os ambientes com exemplos prontos para produção: tratamento de Unicode, variantes URL-safe, codificação de arquivos e respostas de API, uso da CLI e os quatro erros que causam bugs consistentemente em bases de código reais.

  • btoa() é nativo do navegador e está disponível no Node.js 16+ globalmente, mas só aceita Latin-1 (pontos de código 0–255) — entrada Unicode lança uma DOMException
  • Buffer.from(text, "utf8").toString("base64") é o equivalente no Node.js e lida com Unicode nativamente sem etapas extras
  • URL-safe Base64 substitui + → -, / → _, e remove o preenchimento = — use Buffer.from().toString("base64url") no Node.js 18+ para fazer isso em uma linha
  • Para dados binários (ArrayBuffer, Uint8Array, arquivos), use Buffer no Node.js ou a abordagem arrayBuffer() + Uint8Array no navegador — nunca response.text()
  • Uint8Array.prototype.toBase64() (TC39 Stage 3) já está disponível no Node.js 22+ e Chrome 130+ e unificará os dois ambientes

O que é Codificação Base64?

Base64 converte dados binários arbitrários em uma string construída a partir de 64 caracteres ASCII imprimíveis: A–Z, a–z, 0–9, +, e /. Cada 3 bytes de entrada são mapeados para exatamente 4 caracteres Base64; se o comprimento da entrada não for múltiplo de 3, um ou dois caracteres de preenchimento = são adicionados. A saída codificada é sempre cerca de 33% maior que o original.

Base64 não é criptografia — não fornece confidencialidade. Qualquer pessoa com a string codificada pode decodificá-la com uma única chamada de função. Seu propósito é a segurança no transporte: muitos protocolos e formatos de armazenamento foram projetados para texto ASCII de 7 bits e não conseguem lidar com bytes binários arbitrários. Base64 preenche essa lacuna. Os casos de uso comuns em JavaScript incluem URIs de dados para incorporar ativos, cabeçalhos HTTP Basic Auth, segmentos de token JWT, anexos MIME de e-mail e armazenamento de blobs binários em APIs JSON.

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

btoa() — A Função de Codificação Nativa do Navegador

btoa() (binary-to-ASCII) está disponível nos navegadores desde o IE10 e tornou-se global no Node.js 16.0 como parte da iniciativa de compatibilidade WinterCG. Também funciona nativamente no Deno, Bun e Cloudflare Workers. Nenhuma importação é necessária.

A função recebe um único argumento de string e retorna sua forma codificada em Base64. A contraparte simétrica atob() (ASCII-to-binary) decodifica de volta. Ambas são síncronas e executam em memória constante em relação ao tamanho da entrada.

Exemplo mínimo funcional

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=

Decodificação com 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
Nota:btoa() e atob() fazem parte da API Mínima Comum do WinterCG — a mesma especificação que governa Fetch, URL e crypto em runtimes não-navegadores. Eles se comportam de forma idêntica no Node.js 16+, Bun, Deno e Cloudflare Workers.

Tratamento de Unicode e Caracteres Não-ASCII

A armadilha mais comum com btoa() é seu estrito limite Latin-1. Qualquer caractere com um ponto de código acima de U+00FF causa uma exceção imediata:

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

A abordagem correta é codificar a string para bytes UTF-8 primeiro, depois codificar esses bytes em Base64. O JavaScript fornece TextEncoder exatamente para esse propósito:

Abordagem com TextEncoder — segura para qualquer entrada Unicode

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 = 'Confirmado: João Silva — armazém São Paulo, qtd: 250'
const encoded   = toBase64(orderNote)
const decoded   = fromBase64(encoded)

console.log(encoded)
// Q29uZmlybWFkbzogSm/Do28gU2lsdmEg4oCTIGFybWF6w6ltIFPDo28gUGF1bG8sIHF0ZDogMjUw

console.log(decoded === orderNote) // true
Nota:Se você já está no Node.js, pule completamente o contorno com TextEncoder — use Buffer.from(text, 'utf8').toString('base64'). Ele lida com Unicode nativamente e é mais rápido para strings grandes.

Buffer.from() no Node.js — Guia Completo com Exemplos

No Node.js, Buffer é a API idiomática para todas as operações com dados binários, incluindo conversões de codificação. Ele precede o TextEncoder por anos e continua sendo a escolha preferida para código do lado do servidor. Principais vantagens sobre btoa(): suporte nativo a UTF-8, tratamento de dados binários e o atalho de codificação 'base64url' disponível desde o Node.js 18.

Codificação e decodificação básica de texto

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

Codificação de arquivos binários do disco

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

Codificação assíncrona de arquivos com tratamento de erros

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

Funções Base64 do JavaScript — Referência de Parâmetros

Ao contrário do módulo base64 do Python, o JavaScript não tem uma única função Base64 unificada. A API depende do ambiente de destino. Aqui está a referência completa para todas as abordagens nativas:

FunçãoTipo de entradaUnicodeURL-safeDisponível em
btoa(string)string (Latin-1)❌ lança acima de U+00FF❌ substituição manualBrowser, Node 16+, Bun, Deno
atob(string)Base64 string❌ retorna string binária❌ substituição manualBrowser, Node 16+, Bun, Deno
Buffer.from(src, enc) .toString(enc)string | Buffer | Uint8Array✅ codificação utf8✅ base64url no Node 18+Node.js, Bun
TextEncoder().encode(str) + btoa()string (qualquer Unicode)✅ via bytes UTF-8❌ substituição manualBrowser, Node 16+, Deno
Uint8Array.toBase64() (TC39)Uint8Array✅ binário✅ omitPadding + alphabetChrome 130+, Node 22+

A assinatura Buffer.from(src, enc).toString(enc) aceita vários valores de codificação relevantes para Base64:

"base64"
Base64 padrão (RFC 4648 §4). Usa + e / com preenchimento =.
"base64url"
Base64 URL-safe (RFC 4648 §5, Node.js 18+). Usa - e _ sem preenchimento.
"utf8"
Padrão para fontes de string. Use quando a fonte é texto legível por humanos.
"binary"
Latin-1 / ISO-8859-1. Usado quando a fonte é uma string binária bruta (ex.: de atob()).

Base64 URL-safe — Codificação para JWTs, URLs e Nomes de Arquivo

O Base64 padrão usa + e /, que são reservados em URLs — + é decodificado como espaço em query strings, e / é um separador de caminho. JWTs, parâmetros de URL, nomes de arquivo e valores de cookie exigem a variante URL-safe: +-, /_, = final removido.

Navegador — substituição manual de caracteres

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+ — codificação nativa 'base64url'

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

Codificação de Arquivos e Respostas de API em JavaScript

No código de produção, a codificação Base64 é aplicada com mais frequência a arquivos sendo transmitidos e a respostas de APIs externas que entregam conteúdo binário. Os padrões diferem entre o navegador e o Node.js, e dados binários exigem cuidado especial.

Navegador — codificar um arquivo de um elemento input

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

Obtendo dados binários codificados em Base64 de uma 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}`)

Quando você só precisa inspecionar uma resposta codificada durante a depuração de API sem configurar um script, cole o valor Base64 diretamente no Codificador Base64 — ele também decodifica, com saída imediata. Útil para inspecionar respostas da API do GitHub, payloads JWT e assinaturas de webhook.

Codificação Base64 por Linha de Comando com Node.js e Shell

Para scripts de CI/CD, targets de Makefile ou depuração pontual, raramente você precisa de um script completo. Tanto as ferramentas do sistema quanto os one-liners do Node.js cobrem a maioria dos casos multiplataforma.

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=="
Nota:No macOS, base64 quebra a saída em 76 caracteres por padrão. Isso quebra a análise posterior. Sempre adicione -b 0 (macOS) ou --wrap=0 (Linux) quando precisar de um resultado em uma única linha — por exemplo, ao gravar em uma variável de ambiente ou em um campo de configuração.

Alternativa de Alto Desempenho: js-base64

As APIs integradas são suficientes para a maioria dos casos de uso. A principal razão para recorrer a uma biblioteca é a consistência entre ambientes: se você publica um pacote que roda tanto no navegador quanto no Node.js, usar Buffer requer detecção de ambiente ou configuração do bundler, enquanto btoa() requer o contorno para Unicode. js-base64 (mais de 100M de downloads semanais no npm) lida com ambos de forma transparente.

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

Internamente, js-base64 usa Buffer nativo quando disponível e recorre a uma implementação em JS puro no navegador. É 2–3× mais rápido que a abordagem TextEncoder+btoa para strings Unicode longas, e a API simétrica (toBase64 / fromBase64) elimina a sobrecarga mental de lembrar em qual direção btoa e atob funcionam.

Codificando Arquivos Binários Grandes com Streams do Node.js

Quando você precisa codificar arquivos maiores que ~50 MB, carregar o arquivo inteiro na memória com readFileSync() torna-se um problema. Os streams do Node.js permitem processar os dados em partes — mas a codificação Base64 tem uma restrição: você deve alimentar o codificador em múltiplos de 3 bytes para evitar preenchimento incorreto nos limites dos chunks.

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')
Nota:O tamanho do chunk deve ser múltiplo de 3 bytes para evitar o preenchimento espúrio = no meio da saída. O exemplo usa 3 * 1024 * 256 = 786.432 bytes (768 KB) — ajuste highWaterMark conforme seu orçamento de memória. Para arquivos abaixo de 50 MB, readFile() + Buffer.toString('base64') é mais simples e suficientemente rápido.

Erros Comuns

Já revisei muitas bases de código JavaScript com codificação Base64, e esses quatro erros aparecem de forma consistente — muitas vezes não descobertos até que um caractere não-ASCII ou um arquivo binário chegue ao caminho de codificação em produção.

Erro 1 — Passar Unicode diretamente para btoa()

Problema: btoa() só aceita caracteres com pontos de código 0–255. Caracteres como ñ, emojis ou ideogramas CJK causam uma DOMException imediata. Solução: codifique com TextEncoder primeiro, ou use Buffer.from(text, 'utf8').toString('base64') no Node.js.

Before · JavaScript
After · JavaScript
// ❌ DOMException: The string to be encoded contains
//    characters outside of the Latin1 range
const username = 'Camila Rocha'
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('Camila Rocha')
// Q2FtaWxhIFJvY2hh

Erro 2 — Esquecer de restaurar o preenchimento antes de atob()

Problema: O Base64 URL-safe remove o preenchimento =. Passar a string sem preenchimento diretamente para atob() produz saída incorreta ou lança uma exceção dependendo do comprimento da string. Solução: restaure + e / e readicione a quantidade correta de preenchimento antes de chamar 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"}

Erro 3 — Concatenar chunks codificados em vez de buffers brutos

Problema: Cada chamada para btoa() ou .toString('base64') adiciona seu próprio preenchimento. Concatenar duas strings Base64 com preenchimento produz saída inválida porque o preenchimento só deve estar no final. Solução: concatene os dados brutos antes de codificar.

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

Erro 4 — Usar response.text() para ler dados binários de API antes de codificar

Problema: response.text() interpreta os bytes brutos como UTF-8 e substitui sequências de bytes não reconhecidas pelo caractere de substituição U+FFFD. Qualquer conteúdo binário — imagens, PDFs, áudio — é silenciosamente corrompido antes de chegar a btoa(). Solução: use response.arrayBuffer() para obter bytes brutos.

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

Métodos Base64 do JavaScript — Comparação Rápida

MétodoUnicodeDados bináriosURL-safeAmbientesRequer instalação
btoa() / atob()❌ Latin-1❌ workaround necessário❌ substituição manualBrowser, Node 16+, Bun, DenoNão
TextEncoder + btoa()✅ UTF-8✅ via Uint8Array❌ substituição manualBrowser, Node 16+, DenoNão
Buffer.from().toString()✅ utf8✅ nativo✅ base64url (Node 18+)Node.js, BunNão
Uint8Array.toBase64() (TC39)✅ binário✅ nativo✅ opção alphabetChrome 130+, Node 22+Não
js-base64✅ sempre✅ Uint8Array✅ integradoUniversalnpm install

Escolha btoa() apenas quando a entrada é comprovadamente só ASCII — resumos hexadecimais, IDs numéricos ou strings Latin-1 pré-validadas. Para texto fornecido pelo usuário em um navegador, use TextEncoder + btoa(). Para todo o código do lado do servidor no Node.js, Buffer é o padrão correto. Para bibliotecas que precisam rodar em ambos os ambientes sem configuração do bundler, js-base64 elimina todos os casos extremos.

Perguntas Frequentes

Por que btoa() lança "InvalidCharacterError" na minha string?
btoa() só aceita caracteres com pontos de código no intervalo 0–255 (Latin-1 / ISO-8859-1). Qualquer caractere acima de U+00FF — incluindo a maioria do cirílico, árabe, ideogramas CJK e muitos emojis — causa uma DOMException. A solução depende do seu ambiente: no navegador, codifique para bytes UTF-8 com TextEncoder primeiro, converta cada byte em um caractere com String.fromCharCode(), depois chame btoa(). No Node.js, use Buffer.from(text, 'utf8').toString('base64') que lida com Unicode nativamente.
btoa() está disponível no Node.js sem nenhuma importação?
Sim, desde o Node.js 16.0. Tanto btoa() quanto atob() são registradas como funções globais — nenhuma importação é necessária. Elas se comportam de forma idêntica às suas contrapartes no navegador, incluindo a restrição de Latin-1. Para código de servidor no Node.js, Buffer.from() ainda é preferível a btoa() porque lida com UTF-8 nativamente, suporta dados binários sem contornos e tem a opção de codificação 'base64url' adicionada no Node.js 18.
Qual é a diferença entre Base64 padrão e Base64 URL-safe?
O Base64 padrão (RFC 4648 §4) usa + para o valor 62, / para o valor 63 e = para preenchimento. Esses caracteres têm significado especial em URLs: + é interpretado como espaço em query strings, e / é um separador de caminho. O Base64 URL-safe (RFC 4648 §5) substitui - por + e _ por /, e normalmente omite o preenchimento = completamente. JWTs usam Base64 URL-safe para todos os três segmentos. No Node.js 18+, Buffer.from(text).toString('base64url') produz o formato URL-safe diretamente.
Como codifico uma imagem em Base64 para uma URI de dados CSS em JavaScript?
No navegador: use file.arrayBuffer() para ler o binário, converta para Uint8Array, depois chame btoa(Array.from(bytes, b => String.fromCharCode(b)).join('')). Construa a URI de dados como 'data:' + file.type + ';base64,' + encoded. No Node.js: const encoded = fs.readFileSync('./image.png').toString('base64') e prefixe o tipo MIME. Para arquivos SVG você geralmente pode pular o Base64 completamente e usar uma URI de dados codificada por URL, que é mais legível e ligeiramente menor.
Posso codificar e decodificar em Base64 sem nenhuma biblioteca npm no navegador?
Sim. Para entrada só ASCII, btoa() e atob() funcionam diretamente. Para Unicode, o par TextEncoder / TextDecoder fornece o conjunto completo de ferramentas — ambos estão integrados em todos os navegadores modernos e Node.js 16+. O único caso onde uma biblioteca agrega valor genuinamente é a consistência entre ambientes: se você escreve um utilitário que deve funcionar de forma idêntica tanto no navegador quanto no Node.js sem configuração do bundler, js-base64 remove a lógica de detecção de ambiente.
Como decodifico conteúdo Base64 da API do GitHub?
A API de Conteúdos do GitHub retorna o conteúdo do arquivo como Base64 com caracteres de nova linha incorporados (a API quebra a saída em 60 chars). Remova-os antes de decodificar: const clean = data.content.replace(/\n/g, ''); const text = atob(clean);. No Node.js: const text = Buffer.from(data.content.replace(/\n/g, ''), 'base64').toString('utf8');. O GitHub sempre usa Base64 padrão (não URL-safe), portanto nenhuma substituição de + → - ou / → _ é necessária.

Ferramentas Relacionadas

Para codificar ou decodificar com um clique sem escrever nenhum código, cole sua string ou binário diretamente no Codificador Base64 — ele lida com os modos padrão e URL-safe instantaneamente no seu navegador.

Também disponível em: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 LaurentRevisor técnico

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.