---
title: "base64 em Python: codificar, decodificar e transportar dados binários"
url: "https://python.dev.br/blog/python-base64-codificar-decodificar/"
markdown_url: "https://python.dev.br/blog/python-base64-codificar-decodificar.MD"
description: "Aprenda base64 em Python para codificar bytes, embutir imagens, montar Basic Auth, usar URL-safe e evitar erros comuns de padding e confusão com criptografia."
date: "2026-08-04"
author: "Equipe Python Brasil"
---

# base64 em Python: codificar, decodificar e transportar dados binários

Aprenda base64 em Python para codificar bytes, embutir imagens, montar Basic Auth, usar URL-safe e evitar erros comuns de padding e confusão com criptografia.


O módulo `base64` resolve um problema cotidiano de backend e automação: **como colocar bytes em um canal que só aceita texto**. JSON, headers HTTP, e-mail MIME, variáveis de ambiente e muitos webhooks não transportam binário cru de forma confiável. Base64 transforma esses bytes em um alfabeto ASCII estável e, no caminho inverso, recupera o conteúdo original.

A recomendação direta é: use `base64.b64encode()` e `base64.b64decode()` para a maioria dos casos; use a variante `urlsafe_*` quando o valor for para URL; trabalhe sempre com `bytes`, não com `str` misturado; e nunca trate base64 como criptografia. Para tokens imprevisíveis, continue em [`secrets`](/blog/python-secrets-tokens-senhas-seguros/); para checksums e integridade, use [`hashlib`](/blog/python-hashlib-checksums-integridade/); para IDs públicos, use [`uuid`](/blog/python-uuid-identificadores-unicos/).

Neste guia, você vai codificar e decodificar strings e arquivos, embutir imagens em data URL, montar Basic Auth, entender padding, escolher entre base64 clássico e URL-safe e evitar os erros que mais quebram integrações no Brasil — de QR Code PIX a anexos de e-mail e payloads de API.

## O que base64 resolve — e o que ele não resolve

Base64 mapeia grupos de 3 bytes (24 bits) para 4 caracteres de um alfabeto de 64 símbolos. O resultado cresce cerca de 33% em relação ao binário original, mas passa por sistemas que rejeitam bytes de controle, nulos ou acentos mal interpretados.

Isso é útil quando você precisa:

- embutir uma imagem pequena em JSON ou HTML (`data:image/png;base64,...`);
- transportar um certificado, chave pública ou PDF em uma API de texto;
- montar o header `Authorization: Basic ...`;
- representar um hash ou token em um formato legível;
- decodificar QR Codes, webhooks e respostas de PSP de pagamento.

Base64 **não** esconde o conteúdo. Qualquer pessoa com a string decodifica em milissegundos. Base64 **não** autentica origem. Base64 **não** substitui HTTPS, HMAC nem assinatura digital. Se a string vazou, o segredo vazou.

## Codificar e decodificar texto em Python

O fluxo mais comum começa com texto Unicode convertido para bytes UTF-8:

```python
import base64

texto = "Pedido #1042 — São Paulo"
dados = texto.encode("utf-8")

codificado = base64.b64encode(dados)
print(codificado)
# b'UGVkaWRvICMxMDQyIOKAkyBTw6NvIFBhdWxv'

# Para APIs e logs, a forma textual é o usual:
como_texto = codificado.decode("ascii")
print(como_texto)
```

No caminho inverso:

```python
import base64

recebido = "UGVkaWRvICMxMDQyIOKAkyBTw6NvIFBhdWxv"
bytes_originais = base64.b64decode(recebido)
texto = bytes_originais.decode("utf-8")

print(texto)
# Pedido #1042 — São Paulo
```

Três regras evitam 90% dos bugs:

1. `b64encode` recebe e devolve **`bytes`**.
2. Se a API pede string, faça `.decode("ascii")` depois de codificar.
3. Ao decodificar, o resultado continua sendo `bytes` até você escolher o encoding do conteúdo (UTF-8, Latin-1, binário puro).

Se o payload for JSON, prefira serializar com `json.dumps` e só então codificar bytes — veja o guia de [JSON em Python](/blog/trabalhando-com-json-python/).

## Arquivos, imagens e data URL

Para um arquivo binário, leia em modo `rb` e codifique o conteúdo inteiro ou em blocos se o tamanho for grande:

```python
import base64
from pathlib import Path

caminho = Path("comprovante.png")
conteudo = caminho.read_bytes()
b64 = base64.b64encode(conteudo).decode("ascii")

data_url = f"data:image/png;base64,{b64}"
print(data_url[:80], "...")
```

Data URLs são comuns em HTML de e-mail, pré-visualização de upload e respostas de APIs de QR Code. No ecossistema brasileiro de pagamentos, vários PSPs devolvem o QR Code PIX já em base64 — o padrão aparece no guia de [integração de pagamentos PIX com Python](/blog/integracao-pagamentos-pix-python/).

Para gravar de volta em disco:

```python
import base64
from pathlib import Path

recebido = "iVBORw0KGgoAAAANSUhEUg..."  # truncado no exemplo
Path("saida.png").write_bytes(base64.b64decode(recebido))
```

Use [`pathlib`](/blog/python-pathlib-manipulacao-caminhos-arquivos/) para caminhos e, se o arquivo for intermediário de um pipeline, considere [`tempfile`](/blog/python-tempfile-arquivos-temporarios/) para não deixar lixo no sistema.

## Basic Auth e headers HTTP

O esquema Basic concatena `usuario:senha`, codifica em base64 e envia no header. Em produção, isso só faz sentido sobre HTTPS — e, mesmo assim, tokens de API ou OAuth costumam ser melhores.

```python
import base64

usuario = "agencia"
senha = "segredo-temporario"
token = base64.b64encode(f"{usuario}:{senha}".encode("utf-8")).decode("ascii")

headers = {
    "Authorization": f"Basic {token}",
    "Accept": "application/json",
}
```

Com `httpx` ou `requests`, muitas vezes você não monta o header na mão:

```python
import httpx

with httpx.Client(timeout=20.0) as client:
    resposta = client.get(
        "https://httpbin.org/basic-auth/agencia/segredo-temporario",
        auth=("agencia", "segredo-temporario"),
    )
    print(resposta.status_code)
```

Para timeouts, retries e clientes modernos, veja [`httpx` em Python](/blog/python-httpx-requests-moderno/) e o guia de [timeouts e retries com httpx](/blog/httpx-timeouts-retries-python/).

## URL-safe: quando `+` e `/` quebram o fluxo

O alfabeto clássico inclui `+` e `/`. Em query strings, esses caracteres precisam de percent-encoding e frequentemente chegam alterados. A variante URL-safe troca:

- `+` → `-`
- `/` → `_`

```python
import base64

payload = b'{"pedido":1042,"uf":"SP"}'

classico = base64.b64encode(payload).decode("ascii")
urlsafe = base64.urlsafe_b64encode(payload).decode("ascii")

print(classico)
print(urlsafe)
```

Na decodificação, use a função correspondente:

```python
import base64

valor = "eyJwZWRpZG8iOjEwNDIsInVmIjoiU1AifQ=="
print(base64.urlsafe_b64decode(valor))
```

Para tokens de recuperação de senha e links de uso único, a escolha mais simples e segura costuma ser `secrets.token_urlsafe(32)`, que já devolve texto URL-safe com aleatoriedade forte. Base64 entra quando você precisa **representar bytes existentes** (um ciphertext, um protobuf, um arquivo), não quando o objetivo é **gerar** um segredo.

## Padding (`=`), whitespace e o erro Incorrect padding

Base64 exige que o comprimento da string seja múltiplo de 4. Quando faltam bytes no final, a codificação acrescenta um ou dois `=`. Algumas APIs e JWT omitem o padding; outras inserem quebras de linha a cada 76 caracteres (MIME).

```python
import base64
import re

def decodificar_flexivel(valor: str) -> bytes:
    limpo = re.sub(r"\s+", "", valor)
    # completa padding se a origem omitiu os '='
    resto = len(limpo) % 4
    if resto:
        limpo += "=" * (4 - resto)
    return base64.b64decode(limpo, validate=False)
```

Boas práticas:

- remova espaços e quebras de linha antes de decodificar;
- se a origem garante padding e alfabeto corretos, use `validate=True` para falhar cedo;
- nunca decodifique cegamente um blob gigante vindo da internet sem limite de tamanho;
- trate `binascii.Error` e responda com 400 em APIs, não com 500 genérico.

## base64 e e-mail MIME

Anexos de e-mail historicamente viajam em base64. O ecossistema de `email` do Python já cuida disso em níveis mais altos, mas entender a camada ajuda a depurar:

```python
import base64
from email.message import EmailMessage

msg = EmailMessage()
msg["Subject"] = "Relatório mensal"
msg["From"] = "financeiro@empresa.com.br"
msg["To"] = "cliente@empresa.com.br"
msg.set_content("Segue o PDF em anexo.")

pdf = open("relatorio.pdf", "rb").read()
msg.add_attachment(
    pdf,
    maintype="application",
    subtype="pdf",
    filename="relatorio.pdf",
)
```

Por baixo, o anexo é transferido com content-transfer-encoding base64. Para automação completa de SMTP, templates e anexos, veja [automação de e-mails com Python](/blog/python-para-automacao-de-emails/).

## Tabela rápida de decisões

| Situação | Função / abordagem |
|---|---|
| Bytes → texto em JSON/API | `base64.b64encode(dados).decode("ascii")` |
| Texto base64 → bytes | `base64.b64decode(texto)` |
| Valor em URL ou path | `urlsafe_b64encode` / `urlsafe_b64decode` |
| Token aleatório seguro | `secrets.token_urlsafe` (não invente com `random`) |
| Integridade de arquivo | `hashlib.sha256` + comparação segura |
| Identificador público | `uuid4` / `uuid7` |
| Esconder segredo | HTTPS + criptografia / cofre — **não** base64 |

## Exemplo prático: webhook que recebe arquivo em base64

Um padrão comum em ERPs, CRMs e fintechs brasileiras é receber o documento já codificado no JSON:

```python
import base64
import json
from pathlib import Path

def salvar_documento_do_webhook(corpo: str, destino: Path, limite_bytes: int = 5_000_000) -> Path:
    payload = json.loads(corpo)
    nome = Path(payload["filename"]).name  # evita path traversal
    bruto = base64.b64decode(payload["content_base64"], validate=True)

    if len(bruto) > limite_bytes:
        raise ValueError("arquivo excede o limite permitido")

    caminho = destino / nome
    caminho.write_bytes(bruto)
    return caminho
```

Pontos de segurança do exemplo:

- `Path(...).name` descarta diretórios maliciosos no nome;
- `validate=True` rejeita alfabeto inválido cedo;
- limite de tamanho evita esgotar disco e memória;
- o conteúdo continua não confiável depois de salvo — não execute o arquivo e não confie só na extensão.

Para orquestrar ferramentas externas depois do download, combine com [`subprocess`](/blog/python-subprocess-comandos-externos/) e, se for montar linha de comando, com [`shlex`](/blog/python-shlex-comandos-shell-seguros/).

## Erros comuns (e como evitar)

**1. Confundir codificação com criptografia.**  
Colocar uma senha em base64 no banco é o mesmo que salvá-la quase em texto puro. Use hash de senha adequado e, para tokens, `secrets`.

**2. Codificar a string sem `.encode()`.**  
`b64encode` não aceita `str`. Defina o encoding explicitamente (`utf-8` na maioria dos casos brasileiros com acentos).

**3. Misturar variantes clássica e URL-safe.**  
Se a origem usou `urlsafe_b64encode`, decodifique com `urlsafe_b64decode`. Trocar as funções gera padding/alfabeto inválido.

**4. Logar o payload completo.**  
Data URLs e PDFs em base64 estouram logs e podem vazar PII. Registre tamanho, hash curto e correlacionador — não o blob inteiro.

**5. Assumir que “parece texto” é texto.**  
Depois de `b64decode`, você tem bytes. Só chame `.decode("utf-8")` se o contrato da API garantir Unicode; caso contrário, trate como binário.

## Quando base64 não é a melhor ferramenta

- **Arquivos grandes entre serviços:** prefira upload direto para S3/R2/MinIO e troque só a URL — veja [upload de arquivos com Python](/blog/python-s3-r2-minio-upload-arquivos/).
- **Streaming de mídia:** base64 em JSON aumenta payload e memória; use multipart ou object storage.
- **Segredos de aplicação:** variáveis de ambiente e cofres (não base64 de senha hardcoded).
- **Comparação de conteúdo:** use hash (`hashlib`), não a string base64, para fingerprints estáveis e curtos.

## Checklist de produção

1. Defina se o canal exige texto (JSON, header, env) ou se aceita binário/multipart.
2. Escolha clássico vs URL-safe de propósito, e documente no contrato da API.
3. Trate padding e whitespace de forma explícita na borda do sistema.
4. Limite tamanho antes e depois de decodificar.
5. Nunca use base64 como controle de acesso ou confidencialidade.
6. Combine com HTTPS, autenticação real e, quando fizer sentido, HMAC ou assinatura.
7. Cubra o fluxo com testes de ida e volta (`encode → decode == original`) e de entrada inválida.

## Perguntas frequentes

### Para que serve o módulo base64 em Python?

Para representar dados binários como texto ASCII portátil. Ele aparece em anexos, data URLs, Basic Auth, webhooks e respostas de pagamento. Não protege o conteúdo contra leitura.

### base64 é a mesma coisa que criptografia?

Não. Qualquer um decodifica base64 sem chave. Criptografia, tokens e hashes resolvem problemas diferentes e devem ser combinados com consciência.

### Qual a diferença entre b64encode e urlsafe_b64encode?

A variante URL-safe evita `+` e `/`, reduzindo atrito em query strings e paths. Use a mesma família na codificação e na decodificação.

### Por que aparece Incorrect padding?

String truncada, whitespace residual ou padding `=` omitido. Normalize a entrada e complete o comprimento para múltiplo de 4 quando o contrato permitir.

### Preciso instalar alguma biblioteca?

Não. `import base64` basta. O restante do stack (HTTP, e-mail, storage, validação) é que pode puxar dependências externas.

## Próximos passos

Se você está montando um backend ou automação em Python no Brasil, encaixe base64 no lugar certo da cadeia:

- gere segredos com [`secrets`](/blog/python-secrets-tokens-senhas-seguros/);
- verifique integridade com [`hashlib`](/blog/python-hashlib-checksums-integridade/);
- identifique recursos com [`uuid`](/blog/python-uuid-identificadores-unicos/);
- troque JSON com o [guia de JSON](/blog/trabalhando-com-json-python/);
- envie documentos por [e-mail](/blog/python-para-automacao-de-emails/) ou [object storage](/blog/python-s3-r2-minio-upload-arquivos/).

Base64 é uma ferramenta de **transporte e representação**. Usada assim, ela some do caminho e deixa o sistema falar bytes com canais que só entendem texto — sem a falsa sensação de segurança que tanto atrapalha código de produção.
