Decodificação de Base64 em Go aparece o tempo todo — inspeção de JWT, anexos de arquivos binários, payloads de APIs de serviços em nuvem. O pacote padrão encoding/base64 do Go resolve tudo isso, mas escolher a variante de codificação errada (padrão vs URL-safe, com padding vs sem padding) é a fonte mais comum de erros “illegal base64 data”. Este guia cobre StdEncoding, URLEncoding, RawURLEncoding, decodificação em streaming com base64.NewDecoder, inspeção de payload JWT e quatro erros que pegam quase todo mundo na primeira vez. Para decodificações pontuais no navegador, o decodificador Base64 do ToolDeck resolve na hora sem escrever uma linha de código.
- ✓encoding/base64 faz parte da biblioteca padrão do Go — nenhum go get necessário
- ✓Use RawURLEncoding para tokens JWT e a maioria das APIs modernas (sem padding, alfabeto URL-safe)
- ✓StdEncoding usa + e / com padding =; URLEncoding troca por - e _ mas mantém o padding
- ✓base64.NewDecoder envolve qualquer io.Reader para decodificação em streaming sem carregar na memória
- ✓Sempre verifique o erro retornado — padding inválido e alfabeto errado produzem illegal base64 data
O que é Decodificação Base64?
A codificação Base64 representa dados binários como texto ASCII usando 64 caracteres imprimíveis (A–Z, a–z, 0–9, mais dois extras). A decodificação reverte isso — converte essa representação ASCII de volta nos bytes originais. Cada 4 caracteres Base64 decodificam para exatamente 3 bytes. O esquema existe porque muitas camadas de transporte (e-mail, cabeçalhos HTTP, campos JSON) são projetadas para texto, não para binário bruto. Veja como a ida e volta funciona:
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Bytes brutos → codificados em Base64 → decodificados de volta para bytes brutos
original := []byte("service_token:xK9mP2qR")
// Codificado: "c2VydmljZV90b2tlbjp4SzltUDJxUg=="
encoded := base64.StdEncoding.EncodeToString(original)
decoded, _ := base64.StdEncoding.DecodeString(encoded)
fmt.Println(string(decoded) == string(original)) // true
}Decodificar Base64 em Go com encoding/base64
O pacote encoding/base64 vem com o Go — sem dependências externas. Ele expõe quatro variantes de codificação pré-definidas como variáveis no nível do pacote. A função mais usada para entrada do tipo string é DecodeString, que retorna um slice de bytes e um erro.
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
// Base64 padrão — o alfabeto usa + e / com padding =
encoded := "eyJob3N0IjoiZGItcHJvZC51cy1lYXN0LTEiLCJwb3J0Ijo1NDMyfQ=="
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode error: %v", err)
}
fmt.Println(string(decoded))
// {"host":"db-prod.us-east-1","port":5432}
}O método Decode opera em slices de bytes em vez de strings e escreve o resultado em um buffer de destino pré-alocado. É necessário dimensionar o buffer corretamente — use base64.StdEncoding.DecodedLen(len(src)) para obter o tamanho máximo (pode ser alguns bytes maior que o comprimento real decodificado devido ao padding).
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
src := []byte("eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9")
dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(src)))
n, err := base64.RawStdEncoding.Decode(dst, src)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(dst[:n]))
// {"host":"db-prod","port":5432}
}DecodedLen retorna um limite superior, não o comprimento exato. Sempre use o valor de retorno n de Decode para fatiar o resultado corretamente: dst[:n].StdEncoding vs URLEncoding — Escolhendo a Variante Certa
É aqui que mora a maior parte da confusão. O encoding/base64 do Go expõe quatro objetos de codificação, e escolher o errado garante um erro. A diferença se resume a dois fatores: o alfabeto e o padding.
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Payload do cabeçalho JWT — URL-safe, sem padding
jwtHeader := "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjMtMDkifQ"
// Errado: StdEncoding falha em entrada URL-safe sem padding
_, err1 := base64.StdEncoding.DecodeString(jwtHeader)
fmt.Println("StdEncoding error:", err1)
// StdEncoding error: illegal base64 data at input byte 43
// Correto: RawURLEncoding — sem padding, alfabeto URL-safe
decoded, err2 := base64.RawURLEncoding.DecodeString(jwtHeader)
fmt.Println("RawURLEncoding ok:", err2, "→", string(decoded))
// RawURLEncoding ok: <nil> → {"alg":"RS256","kid":"2023-09"}
}As quatro variantes em termos simples:
Minha regra prática: se veio de um JWT, fluxo OAuth ou SDK de provedor de nuvem, use RawURLEncoding primeiro. Se veio de anexos de e-mail ou formulários web antigos, tente StdEncoding. A mensagem de erro sempre indica a posição exata do byte onde a decodificação falhou.
Decodificar Base64 de um Arquivo e Resposta de API
Lendo um arquivo codificado em Base64
Arquivos binários (imagens, PDFs, certificados) às vezes são armazenados em disco codificados em Base64. Leia o arquivo, remova espaços em branco no final e então decodifique:
package main
import (
"encoding/base64"
"fmt"
"log"
"os"
"strings"
)
func main() {
raw, err := os.ReadFile("certificate.pem.b64")
if err != nil {
log.Fatalf("read file: %v", err)
}
// Remove quebras de linha — arquivos Base64 geralmente têm quebras a cada 76 chars
cleaned := strings.ReplaceAll(strings.TrimSpace(string(raw)), "\n", "")
decoded, err := base64.StdEncoding.DecodeString(cleaned)
if err != nil {
log.Fatalf("decode: %v", err)
}
if err := os.WriteFile("certificate.pem", decoded, 0600); err != nil {
log.Fatalf("write: %v", err)
}
fmt.Printf("decoded %d bytes → certificate.pem\n", len(decoded))
}Decodificando um campo Base64 de uma resposta JSON de API
APIs de nuvem frequentemente retornam dados binários (chaves de criptografia, blobs assinados, miniaturas) como strings Base64 dentro de JSON. Faça o unmarshal do JSON primeiro, depois decodifique o campo desejado:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
)
type SecretResponse struct {
Name string `json:"name"`
Payload string `json:"payload"` // valor secreto codificado em Base64
Version int `json:"version"`
}
func fetchAndDecodeSecret(secretURL string) ([]byte, error) {
resp, err := http.Get(secretURL)
if err != nil {
return nil, fmt.Errorf("http get: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var secret SecretResponse
if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil {
return nil, fmt.Errorf("decode json: %w", err)
}
value, err := base64.StdEncoding.DecodeString(secret.Payload)
if err != nil {
return nil, fmt.Errorf("decode base64: %w", err)
}
return value, nil
}
func main() {
value, err := fetchAndDecodeSecret("https://api.example.com/secrets/db-password")
if err != nil {
log.Fatalf("fetch secret: %v", err)
}
fmt.Printf("secret value: %s\n", value)
}fmt.Errorf("decode base64: %w", err) em vez de perder o contexto. A mensagem de erro original de encoding/base64 inclui a posição do byte onde ocorreu a falha, o que é útil durante a depuração.Streaming de Arquivos Grandes Codificados em Base64
Carregar um arquivo de 500 MB codificado em Base64 na memória com os.ReadFile e depois chamar DecodeString usa cerca de 750 MB de RAM (a string codificada mais os bytes decodificados). base64.NewDecoder envolve qualquer io.Reader e decodifica em partes, mantendo o uso de memória praticamente constante. Combine com io.Copy para um pipeline de streaming limpo:
package main
import (
"encoding/base64"
"fmt"
"io"
"log"
"os"
)
func streamDecodeFile(srcPath, dstPath string) error {
src, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("open source: %w", err)
}
defer src.Close()
dst, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("create dest: %w", err)
}
defer dst.Close()
decoder := base64.NewDecoder(base64.StdEncoding, src)
written, err := io.Copy(dst, decoder)
if err != nil {
return fmt.Errorf("stream decode: %w", err)
}
fmt.Printf("written %d bytes to %s\n", written, dstPath)
return nil
}
func main() {
if err := streamDecodeFile("backup.tar.b64", "backup.tar"); err != nil {
log.Fatal(err)
}
}base64.NewDecoder espera dados Base64 limpos e sem interrupções. Se o arquivo fonte tiver quebras de linha (comum em arquivos PEM e MIME), envolva o reader fonte com um reader que remova as linhas, ou pré-processe o arquivo para remover as quebras antes do streaming.Decodificação Base64 pela Linha de Comando
A maioria dos desenvolvedores Go recorre à linha de comando primeiro ao depurar. Todo macOS e Linux já vem com base64; no Windows, o PowerShell tem um equivalente embutido. Para inspeção rápida de payloads de API, isso é mais rápido do que escrever um script Go.
# Decodificar uma string Base64 (Linux / macOS)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode
# {"host":"db-prod","port":5432}
# Decodificar e formatar com jq (redirecionar a saída JSON)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode | jq .
# {
# "host": "db-prod",
# "port": 5432
# }
# Decodificar um arquivo Base64 para binário
base64 --decode < encrypted_payload.b64 > encrypted_payload.bin
# macOS usa o flag -D em vez de --decode
echo "c2VjcmV0LXRva2Vu" | base64 -DPara inspecionar tokens JWT sem nenhuma ferramenta instalada, cole o token no decodificador Base64 do ToolDeck — divida nos pontos e decodifique cada parte.
Alternativa de Alta Performance: encoding/base64 já é rápido
Ao contrário do Python, onde orjson vs json é uma conversa relevante sobre performance, o encoding/base64 do Go já é otimizado em assembly e genuinamente rápido para a maioria das cargas de trabalho. Dito isso, se você estiver processando milhões de registros em um loop intenso, filippo.io/base64 oferece decodificação acelerada por SIMD com uma API idêntica.
go get filippo.io/base64
package main
import (
"fmt"
"log"
"filippo.io/base64"
)
func main() {
// Substituto direto — mesma API que encoding/base64
encoded := "eyJob3N0IjoiY2FjaGUtcHJvZCIsInBvcnQiOjYzNzl9"
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(decoded))
// {"host":"cache-prod","port":6379}
}O ganho de performance é mais visível em amd64 com suporte AVX2 — benchmarks mostram melhoria de 2–4x na taxa de transferência em entradas grandes. Para decodificação cotidiana de respostas de API (alguns centenas de bytes por vez), fique com a biblioteca padrão.
Decodificando o Payload JWT Base64 em Go
Inspeção de JWT aparece em quase todo serviço backend. Na minha experiência, a maioria das sessões de depuração se resume a “o que tem nesse token?” — e você pode responder isso sem precisar de uma biblioteca JWT completa. O token tem três segmentos codificados em Base64url separados por pontos. O segmento do meio é o payload que você realmente precisa:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strings"
)
type JWTPayload struct {
Subject string `json:"sub"`
Issuer string `json:"iss"`
Expiry int64 `json:"exp"`
Roles []string `json:"roles"`
}
func decodeJWTPayload(token string) (*JWTPayload, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT: expected 3 segments, got %d", len(parts))
}
// JWT usa RawURLEncoding — alfabeto URL-safe, sem padding =
raw, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("decode payload: %w", err)
}
var payload JWTPayload
if err := json.Unmarshal(raw, &payload); err != nil {
return nil, fmt.Errorf("unmarshal payload: %w", err)
}
return &payload, nil
}
func main() {
token := "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNDQyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJhdWRpdG9yIl19.SIGNATURE"
payload, err := decodeJWTPayload(token)
if err != nil {
log.Fatalf("jwt: %v", err)
}
fmt.Printf("Subject: %s\n", payload.Subject)
fmt.Printf("Issuer: %s\n", payload.Issuer)
fmt.Printf("Roles: %v\n", payload.Roles)
// Subject: usr-442
// Issuer: auth.example.com
// Roles: [admin auditor]
}Erros Comuns
Encontrei todos esses quatro em revisões de código reais — os dois primeiros aparecem quase sempre que alguém integra um novo provedor de autenticação.
Problema: Tokens JWT e OAuth usam o alfabeto Base64 URL-safe (- e _). Passá-los para StdEncoding.DecodeString falha com 'illegal base64 data'.
Solução: Verifique a origem da sua entrada: tokens de sistemas de autenticação usam RawURLEncoding; anexos binários usam StdEncoding.
// Cabeçalho JWT — URL-safe, sem padding token := "eyJhbGciOiJSUzI1NiJ9" decoded, err := base64.StdEncoding.DecodeString(token) // err: illegal base64 data at input byte 19
// Cabeçalho JWT — codificação correta
token := "eyJhbGciOiJSUzI1NiJ9"
decoded, err := base64.RawURLEncoding.DecodeString(token)
// decoded: {"alg":"RS256"}
// err: nilProblema: Decode escreve em um buffer pré-alocado e retorna o número de bytes efetivamente escritos. DecodedLen retorna um limite superior, então o final do buffer pode conter bytes inválidos.
Solução: Sempre fatie o resultado com dst[:n] — não com dst[:len(dst)].
dst := make([]byte, base64.StdEncoding.DecodedLen(len(src))) base64.StdEncoding.Decode(dst, src) fmt.Println(string(dst)) // pode incluir bytes zero no final
dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
n, err := base64.StdEncoding.Decode(dst, src)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(dst[:n])) // correto — apenas os bytes decodificadosProblema: Strings Base64 copiadas de terminais, e-mails ou arquivos de configuração frequentemente têm quebras de linha ou espaços no final. Passá-las diretamente para DecodeString falha no caractere de espaço em branco.
Solução: Chame strings.TrimSpace (e strings.ReplaceAll para quebras de linha embutidas) antes de decodificar.
// Valor lido de um arquivo de config com quebra de linha no final encoded := "c2VydmljZV9rZXk6eEtNcDI=\n" decoded, err := base64.StdEncoding.DecodeString(encoded) // err: illegal base64 data at input byte 24
encoded := "c2VydmljZV9rZXk6eEtNcDI=\n" cleaned := strings.TrimSpace(encoded) decoded, err := base64.StdEncoding.DecodeString(cleaned) // decoded: "service_key:xKMp2" // err: nil
Problema: Chamar string(decoded) em dados binários (imagens, payloads comprimidos) produz strings UTF-8 inválidas. Strings Go podem conter bytes arbitrários, mas algumas operações vão corromper o conteúdo.
Solução: Mantenha dados binários como []byte ao longo do pipeline. Só chame string(decoded) quando o conteúdo decodificado for garantidamente texto.
decoded, _ := base64.StdEncoding.DecodeString(pngBase64)
// Tratar PNG binário como string perde dados
imageStr := string(decoded)
os.WriteFile("image.png", []byte(imageStr), 0644) // pode corromperdecoded, err := base64.StdEncoding.DecodeString(pngBase64)
if err != nil {
log.Fatal(err)
}
// Escrever bytes diretamente — sem conversão para string
os.WriteFile("image.png", decoded, 0644)Comparação de Métodos
Todas as variantes fazem parte da biblioteca padrão — nenhuma dependência externa para qualquer uma delas.
Para tokens JWT e fluxos OAuth: RawURLEncoding. Para anexos de e-mail e dados MIME: StdEncoding. Para grandes arquivos binários de disco ou rede: envolva um reader em base64.NewDecoder — mantém o uso de memória constante independentemente do tamanho do arquivo. Precisa de um alfabeto customizado? base64.NewEncoding(alphabet) cria um novo objeto de codificação para casos de uso mais específicos.
Para verificações rápidas durante o desenvolvimento, o decodificador Base64 online é mais rápido do que subir um programa Go.
Perguntas Frequentes
Como decodifico uma string Base64 em Go?
Importe encoding/base64 e chame base64.StdEncoding.DecodeString(s). Ele retorna ([]byte, error) — sempre verifique o erro. Se a string usar caracteres URL-safe (- e _ em vez de + e /), use base64.URLEncoding.DecodeString. Para tokens JWT e a maioria das APIs modernas, RawURLEncoding (sem padding) é a escolha certa.
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
encoded := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(decoded))
// {"alg":"HS256","typ":"JWT"}
}Qual a diferença entre StdEncoding e URLEncoding em Go?
StdEncoding usa o alfabeto Base64 padrão com os caracteres + e / e padding = — definido no RFC 4648 §4. URLEncoding substitui + por - e / por _, tornando a saída segura em URLs e cabeçalhos HTTP sem percent-encoding — definido no RFC 4648 §5. Use URLEncoding para tokens JWT, tokens OAuth e qualquer dado embutido em query strings.
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Padrão: pode conter os caracteres + / e =
std := base64.StdEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(std) // "aGVsbG8vd29ybGQ="
// URL-safe: substitui + por - e / por _
url := base64.URLEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(url) // "aGVsbG8vd29ybGQ=" (igual — diferença aparece com outros bytes)
// Cabeçalhos JWT nunca têm padding — use RawURLEncoding
raw := base64.RawURLEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(raw) // "aGVsbG8vd29ybGQ" (sem = no final)
}Como corrigir erros "illegal base64 data" em Go?
Esse erro significa que a entrada contém caracteres fora do alfabeto esperado, ou o padding está incorreto. Três causas comuns: usar StdEncoding em entrada URL-safe (troque por URLEncoding), usar um encoder com padding em entrada sem padding (troque por RawStdEncoding/RawURLEncoding), ou espaços/quebras de linha no final. Remova espaços em branco com strings.TrimSpace antes de decodificar.
package main
import (
"encoding/base64"
"fmt"
"log"
"strings"
)
func main() {
// Entrada de um payload de webhook — tem quebras de linha removidas do formato wire
raw := " aGVsbG8gd29ybGQ= \n"
cleaned := strings.TrimSpace(raw)
decoded, err := base64.StdEncoding.DecodeString(cleaned)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decoded)) // hello world
}Como decodifico em streaming um arquivo grande codificado em Base64 em Go?
Use base64.NewDecoder(base64.StdEncoding, reader), que envolve qualquer io.Reader e decodifica sob demanda. Combine com io.Copy para escrever no destino sem carregar o arquivo inteiro na memória. Esse é o padrão para decodificar anexos binários codificados em Base64 ou grandes payloads de dados.
package main
import (
"encoding/base64"
"io"
"log"
"os"
)
func main() {
src, err := os.Open("attachment.b64")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.Create("attachment.bin")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
decoder := base64.NewDecoder(base64.StdEncoding, src)
io.Copy(dst, decoder)
}Posso decodificar o payload de um JWT Base64 em Go sem uma biblioteca JWT?
Sim. Um JWT é composto por três segmentos codificados em Base64url separados por pontos. Divida no "." e decodifique o segundo segmento (índice 1) com base64.RawURLEncoding.DecodeString — cabeçalhos e payloads JWT usam o alfabeto URL-safe sem padding. O segmento de assinatura (índice 2) é binário e normalmente só é necessário para verificação.
package main
import (
"encoding/base64"
"fmt"
"log"
"strings"
)
func main() {
token := "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3ItOTAxIiwicm9sZSI6ImFkbWluIn0.SIG"
parts := strings.Split(token, ".")
if len(parts) < 2 {
log.Fatal("invalid JWT format")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
log.Fatalf("decode payload: %v", err)
}
fmt.Println(string(payload))
// {"sub":"usr-901","role":"admin"}
}Qual codificação devo usar para decodificar dados Base64 de uma resposta de API HTTP?
Verifique a documentação da API ou inspecione a string codificada. Se ela contiver os caracteres + ou / e terminar com =, use StdEncoding. Se usar - e _ sem =, use RawURLEncoding. Na dúvida, tente RawURLEncoding primeiro — a maioria das APIs modernas (OAuth2, JWT, Google Cloud, AWS) usa Base64 URL-safe sem padding.
package main
import (
"encoding/base64"
"strings"
)
// Detecta a variante de codificação a partir da string codificada
func decodeAPIPayload(encoded string) ([]byte, error) {
// Caracteres URL-safe sem padding — comum em APIs modernas
if !strings.Contains(encoded, "+") && !strings.Contains(encoded, "/") {
return base64.RawURLEncoding.DecodeString(encoded)
}
// Base64 padrão com padding
return base64.StdEncoding.DecodeString(encoded)
}Ferramentas Relacionadas
- Codificador Base64 — codifique dados binários ou texto em Base64 no navegador, útil para gerar fixtures de teste para colar nos seus testes unitários Go.
- Decodificador JWT — divida e decodifique os três segmentos JWT de uma vez, com inspeção campo a campo do payload — sem código Go necessário quando você só precisa ler um token durante a depuração.
- Decodificador de URL — decodifique strings com percent-encoding, útil quando respostas de API misturam dados Base64url com parâmetros de query percent-encoded.
- Formatador JSON — após decodificar um payload JWT em Base64 ou uma resposta de API, cole o JSON aqui para formatar e validar a estrutura instantaneamente.