O que é Base64 e por que ele existe
Base64 é um jeito de escrever dados binários arbitrários usando apenas 64 caracteres ASCII imprimíveis: A–Z, a–z, 0–9, + e /, mais = para preenchimento. É uma codificação, ou seja, qualquer pessoa pode revertê-la sem chave. Ele não esconde nada (mais sobre isso em codificação vs criptografia).
O objetivo é fazer os bytes passarem intactos por canais que só aceitam texto. Um arquivo PNG contém bytes como 0x00, 0x1A e 0x89 que um canal de texto pode remover, traduzir ou tratar como código de controle. Escrito em Base64, o mesmo arquivo vira iVBORw0KGgo..., que passa intacto por e-mail, JSON, XML, URLs (com a variante URL-safe), terminais e copiar e colar.
A definição atual é a RFC 4648. Especificações mais antigas para e-mail MIME (RFC 2045) e certificados PEM descrevem o mesmo alfabeto com regras extras sobre o tamanho das linhas. Um resumo das variantes está na página de padrões do Base64.
Por que o e-mail precisou dele
O e-mail dos primeiros tempos foi projetado para texto ASCII de 7 bits. Os servidores de retransmissão da época podiam remover o bit mais alto de cada byte, quebrar linhas longas e reescrever os finais de linha, o que é inofensivo para uma carta e fatal para um arquivo ZIP. O MIME precisava de um jeito de levar qualquer anexo por esse caminho, e precisava de um alfabeto que sobrevivesse a todos os sistemas no trajeto.
O Base64 escolhe seus 64 caracteres por isso. Letras e dígitos são idênticos no ASCII e em quase todos os conjuntos de caracteres legados, + e / são quase tão seguros, e nenhum deles é caractere de controle ou espaço em branco. 64 também é a maior potência de dois que dá para preencher com esse conjunto seguro, e uma potência de dois mantém a aritmética de bits simples. O custo é o tamanho: 6 bits úteis por caractere de 8 bits significam que a saída tem 4/3 da entrada, mais o preenchimento.
O problema dos 8 bits quase sumiu dos servidores de e-mail modernos, mas a mesma restrição aparece em qualquer lugar onde dados binários precisam ficar dentro de um formato de texto. O JSON não tem tipo binário, e atributos HTML, CSS e headers HTTP são todos texto, então o Base64 continua sendo o jeito padrão de colocar bytes neles.
O algoritmo: entram 3 bytes, saem 4 caracteres
O Base64 trabalha com blocos de 24 bits. Esse é o menor número divisível tanto por 8 (um byte) quanto por 6 (um caractere Base64), então 3 bytes correspondem exatamente a 4 caracteres.
Para cada bloco:
- Pegue 3 bytes e escreva-os como 24 bits em sequência.
- Corte esses 24 bits em quatro grupos de 6 bits.
- Leia cada grupo como um número de 0 a 63.
- Procure o número no alfabeto e escreva aquele caractere.
O alfabeto é uma tabela fixa. O índice 0 é A, 25 é Z, 26 é a, 51 é z, 52 é 0, 61 é 9, 62 é + e 63 é /. A tabela completa está na página do alfabeto Base64.
| Faixa | Caracteres | Exemplo |
|---|---|---|
| 0–25 | A–Z | 19 → T |
| 26–51 | a–z | 46 → u |
| 52–61 | 0–9 | 52 → 0 |
| 62 | + | |
| 63 | / |
A decodificação faz os mesmos passos ao contrário: procura cada caractere para obter 6 bits, junta quatro deles em 24 bits e divide isso em 3 bytes.
Exemplo prático: “Man” vira TWFu
O exemplo clássico usa o texto ASCII Man. Em ASCII, M é 77, a é 97 e n é 110.
| M | a | n | |
|---|---|---|---|
| Decimal | 77 | 97 | 110 |
| Binário | 01001101 | 01100001 | 01101110 |
Junte os bits: 010011010110000101101110. Agora corte a cada 6 bits em vez de a cada 8:
| Grupo de 6 bits | 010011 | 010110 | 000101 | 101110 |
|---|---|---|---|---|
| Valor | 19 | 22 | 5 | 46 |
| Caractere | T | W | F | u |
Então Man vira TWFu. Repare que nenhum caractere de saída se alinha com uma letra de entrada. O W é formado pelos últimos 4 bits de M e pelos 2 primeiros bits de a. É por isso que mudar um byte da entrada muda dois caracteres da saída, e por isso não dá para ler Base64 a olho nu mesmo quando a entrada é texto comum.
Dá para conferir em qualquer shell:
echo -n Man | base64
# TWFu
Sem -n, o echo acrescenta um byte de quebra de linha, e Man\n vira TWFuCg==.
Quando a entrada não é múltiplo de 3: preenchimento
Dados reais raramente têm comprimento divisível por 3. O último bloco fica então com 1 ou 2 bytes, e o Base64 o completa com bits zero e sinais =.
Sobram dois bytes, “Ma” → TWE=. Dois bytes são 16 bits. Acrescente dois bits zero para chegar a 18, que são três grupos de 6 bits:
| Grupo de 6 bits | 010011 | 010110 | 000100 | (nenhum) |
|---|---|---|---|---|
| Valor | 19 | 22 | 4 | |
| Caractere | T | W | E | = |
O terceiro grupo é 0001 vindo de a seguido dos dois zeros acrescentados, o que dá 4, ou seja, E. Um = ocupa o lugar do quarto caractere ausente.
Sobra um byte, “M” → TQ==. Um byte são 8 bits. Acrescente quatro bits zero para chegar a 12, que são dois grupos: 010011 (19, T) e 010000 (16, Q). Dois sinais = completam o bloco.
Assim, o número de sinais = diz quantos bytes o último bloco tinha: nenhum significa 3, um significa 2, dois significam 1. Nunca existe um terceiro =, porque um bloco com zero bytes restantes não existe. O guia de preenchimento explica quando o = pode ser omitido e como recolocá-lo.
import base64
for s in [b"Man", b"Ma", b"M"]:
print(s, base64.b64encode(s))
# b'Man' b'TWFu'
# b'Ma' b'TWE='
# b'M' b'TQ=='
O texto precisa virar bytes primeiro
O Base64 codifica bytes. O texto só vira bytes depois que você escolhe uma codificação de caracteres, e essa escolha muda o resultado. A palavra café em UTF-8 tem 5 bytes (o é ocupa dois), então vira Y2Fmw6k=. Em Latin-1 ela tem 4 bytes e vira Y2Fm6Q==.
Essa é a origem de uma armadilha famosa do JavaScript. A função btoa() do navegador trata cada caractere de uma string como um byte, o que é o comportamento do Latin-1:
btoa("café"); // "Y2Fm6Q==" (Latin-1, not UTF-8)
btoa("café ✓"); // throws InvalidCharacterError
Para obter Base64 de UTF-8 em JavaScript, converta para bytes antes com TextEncoder, ou use Buffer no Node.js. A página de código JavaScript mostra os padrões que funcionam, e Uint8Array.toBase64() cobre o método nativo mais novo. O codificador Base64 deste site usa UTF-8 por padrão e permite escolher outro charset.
Onde você encontra Base64
- Anexos de e-mail. Todo anexo num arquivo
.emlbruto é um bloco Base64 quebrado a cada 76 caracteres. Veja Base64 no e-mail. - Data URIs.
data:image/png;base64,iVBORw0KGgo...embute uma imagem diretamente no HTML ou CSS. Quando isso compensa é o tema de desempenho de data URIs. - HTTP Basic Auth. O header
Authorization: Basic dXNlcjpwYXNzé sóuser:passem Base64. - JWTs. As três partes separadas por pontos de um JSON Web Token são Base64URL. Veja Base64 no JWT.
- Arquivos PEM. Certificados e chaves entre as linhas
-----BEGIN ...-----são Base64 de dados DER binários. - APIs e configuração. Secrets do Kubernetes, metadados de nuvem, payloads de webhooks e muitas APIs JSON transportam arquivos ou blobs binários como strings Base64.
Com o tempo você passa a reconhecer os blobs pelos primeiros caracteres: iVBORw0KGgo é um PNG, JVBERi0 é um PDF, /9j/ é um JPEG e eyJ é um JSON começando com {". A página de prefixos comuns de Base64 lista outros. Para ver o que um blob contém, a ferramenta Base64 para arquivo detecta o formato e mostra uma prévia no seu navegador.
Cole o Base64 e veja o texto ou os bytes que ele contém. Roda localmente no seu navegador.
Perguntas frequentes
O Base64 é uma forma de compressão?
Não. Ele sempre deixa os dados maiores: cada 3 bytes de entrada viram 4 caracteres de saída, cerca de 33% a mais. Veja o acréscimo de tamanho do Base64 para a fórmula exata.
Por que se chama Base64?
Cada caractere de saída carrega um de 64 valores possíveis, o que equivale a 6 bits de informação. O nome é o tamanho do alfabeto, do mesmo jeito que hex é base 16.
Qualquer dado pode ser codificado em Base64?
Sim. O algoritmo trabalha com bytes e não se importa com o que eles significam, então texto, imagens, PDFs, chaves e arquivos compactados são codificados do mesmo jeito. O texto precisa antes virar bytes por meio de uma codificação de caracteres, geralmente UTF-8.
A saída do Base64 pode conter espaços ou quebras de linha?
O Base64 puro da RFC 4648, não. Alguns formatos o quebram em linhas, por exemplo o e-mail MIME a cada 76 caracteres e os arquivos PEM a cada 64, e a maioria dos decodificadores ignora esses espaços em branco.