Base64 com acentos: como codificar e decodificar sem erro
O Base64 codifica bytes, não texto, então o que define o resultado é a codificação do texto antes da conversão. Com UTF-8, acentos e emojis voltam iguais. Este guia explica por que o btoa erra com acentos e emojis e mostra como codificar e decodificar Base64 e Base64URL em JavaScript, Python e PHP.
O que o Base64 faz
O Base64 representa uma sequência de bytes usando só 64 caracteres seguros (letras, números, + e /), com = no fim quando falta completar. Ele aumenta o tamanho em cerca de um terço e não protege nada: qualquer pessoa decodifica. Não é criptografia.
Como ele trabalha com bytes, o texto precisa virar bytes antes. Letras com acento ocupam mais de um byte em UTF-8, e é aí que os erros aparecem. Para testar um valor agora, use o codificador de Base64 ou o decodificador de Base64.
Por que o btoa erra com acentos e emojis
No navegador, o btoa trata cada caractere como um byte (até o código 255, o Latin-1). Com btoa("ação") não há erro, mas o resultado é diferente do Base64 em UTF-8 que o resto do mundo espera, e a outra ponta decodifica letras trocadas. Com um caractere fora do Latin-1, como btoa("✓") ou um emoji, ele falha com InvalidCharacterError. O atob tem o mesmo limite na volta.
A solução é converter o texto em bytes UTF-8 com TextEncoder, codificar esses bytes e, na volta, usar TextDecoder:
function codificarBase64(texto) {
const bytes = new TextEncoder().encode(texto);
let binario = '';
bytes.forEach((b) => (binario += String.fromCharCode(b)));
return btoa(binario);
}
function decodificarBase64(base64) {
const binario = atob(base64);
const bytes = Uint8Array.from(binario, (c) => c.charCodeAt(0));
return new TextDecoder().decode(bytes);
} Funciona no navegador e também no Node.js.
Em Node.js
No Node.js o Buffer já cuida da codificação do texto, e o UTF-8 é o padrão:
function codificarBase64(texto) {
return Buffer.from(texto, 'utf8').toString('base64');
}
function decodificarBase64(base64) {
return Buffer.from(base64, 'base64').toString('utf8');
} Em Python e PHP
Em Python, base64.b64encode recebe bytes: chame encode("utf-8") antes e decode("utf-8") depois. Em PHP, as strings já são sequências de bytes, então base64_encode funciona direto, e vale usar o modo estrito no base64_decode.
import base64
def codificar_base64(texto: str) -> str:
return base64.b64encode(texto.encode("utf-8")).decode("ascii")
def decodificar_base64(b64: str) -> str:
return base64.b64decode(b64).decode("utf-8") <?php
function codificarBase64(string $texto): string
{
return base64_encode($texto);
}
function decodificarBase64(string $b64): string
{
$saida = base64_decode($b64, true); // true: recusa caracteres que não são Base64
if ($saida === false) {
throw new InvalidArgumentException('Base64 inválido');
}
return $saida;
} Base64URL: a variante para endereços e tokens
O + e o / têm significado em endereços, então a variante Base64URL troca + por - e / por _ e costuma omitir o = do fim. É o formato usado nas partes de um JWT (veja o guia de JWT). No Node.js, Buffer.toString("base64url") já faz isso. Nas outras linguagens, a troca é simples:
function paraBase64Url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function deBase64Url(url) {
const base64 = url.replace(/-/g, '+').replace(/_/g, '/');
return base64 + '='.repeat((4 - (base64.length % 4)) % 4);
} Converte de e para o Base64 comum. O preenchimento com = é refeito na volta.
import base64
def codificar_base64url(dados: bytes) -> str:
return base64.urlsafe_b64encode(dados).decode("ascii").rstrip("=")
def decodificar_base64url(texto: str) -> bytes:
return base64.urlsafe_b64decode(texto + "=" * (-len(texto) % 4)) <?php
function codificarBase64Url(string $dados): string
{
return rtrim(strtr(base64_encode($dados), '+/', '-_'), '=');
}
function decodificarBase64Url(string $texto): string
{
$b64 = strtr($texto, '-_', '+/');
return base64_decode($b64 . str_repeat('=', (4 - strlen($b64) % 4) % 4), true);
} Cuidados
- Não use Base64 para esconder senhas ou dados sensíveis: ele só muda a representação.
- Ao decodificar um texto de origem desconhecida, trate o erro: valores com caracteres inválidos ou tamanho errado não decodificam.
- Quebras de linha no meio do Base64 (comum em e-mail e certificados) precisam ser removidas antes de decodificar em algumas bibliotecas.
- Se o resultado decodificado não for texto, como uma imagem, trate-o como bytes e não como string.
Achou um erro neste guia? Avise por e-mail.
Perguntas frequentes
Base64 é criptografia?
Não. É só uma forma de representar bytes com caracteres comuns, e qualquer pessoa decodifica sem chave. Para proteger dados, use criptografia de verdade.
Por que o Base64 termina com um ou dois sinais de igual?
O Base64 trabalha em blocos de 3 bytes. Quando o último bloco tem menos que isso, o sinal de igual completa o tamanho: um quando faltou um byte e dois quando faltaram dois. Na variante URL, ele costuma ser omitido.
Qual a diferença entre Base64 e Base64URL?
O Base64URL troca o mais e a barra por hífen e sublinhado, para não atrapalhar endereços e nomes de arquivo, e costuma omitir o preenchimento com igual. O conteúdo codificado é o mesmo.
O Base64 aumenta o tamanho dos dados?
Sim, em cerca de 33%: a cada 3 bytes originais, ele gera 4 caracteres.