Base64 Encoding & Decoding in Go
Go's encoding/base64 package provides multiple encoding types in a consistent API. The key decision is which encoding to use: StdEncoding, URLEncoding, or RawURLEncoding.
Basic Usage
package main
import (
"encoding/base64"
"fmt"
)
func main() {
input := []byte("Hello, world!")
// Encode
encoded := base64.StdEncoding.EncodeToString(input)
fmt.Println(encoded) // "SGVsbG8sIHdvcmxkIQ=="
// Decode
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
panic(err)
}
fmt.Println(string(decoded)) // "Hello, world!"
}
Choosing an Encoding
Go provides four built-in encodings:
base64.StdEncoding // + and /, padded with = base64.URLEncoding // - and _, padded with = base64.RawStdEncoding // + and /, NO padding base64.RawURLEncoding // - and _, NO padding (most common for JWTs)
Rule of thumb: Use StdEncoding for general data encoding. Use RawURLEncoding for JWTs, URLs, and filenames.
URL-Safe Base64
package main
import (
"encoding/base64"
"fmt"
)
func main() {
data := []byte("data that needs URL-safe encoding: a+b/c=d")
// URL-safe with padding
encoded := base64.URLEncoding.EncodeToString(data)
fmt.Println(encoded)
// URL-safe without padding (common for JWTs)
encodedRaw := base64.RawURLEncoding.EncodeToString(data)
fmt.Println(encodedRaw)
// Decode (use matching decoder)
decoded, err := base64.RawURLEncoding.DecodeString(encodedRaw)
if err != nil {
panic(err)
}
fmt.Println(string(decoded))
}
Encoding Files
package main
import (
"encoding/base64"
"os"
)
func encodeFile(src, dst string) error {
data, err := os.ReadFile(src)
if err != nil {
return err
}
encoded := base64.StdEncoding.EncodeToString(data)
return os.WriteFile(dst, []byte(encoded), 0644)
}
func decodeFile(src, dst string) error {
data, err := os.ReadFile(src)
if err != nil {
return err
}
decoded, err := base64.StdEncoding.DecodeString(string(data))
if err != nil {
return err
}
return os.WriteFile(dst, decoded, 0644)
}
Streaming with NewEncoder / NewDecoder
For large files, use the streaming encoder and decoder to avoid loading everything into memory:
package main
import (
"encoding/base64"
"io"
"os"
)
// Stream-encode a file
func streamEncode(input, output string) error {
in, _ := os.Open(input)
out, _ := os.Create(output)
defer in.Close()
defer out.Close()
encoder := base64.NewEncoder(base64.StdEncoding, out)
io.Copy(encoder, in)
return encoder.Close() // IMPORTANT: flush final bytes
}
// Stream-decode a file
func streamDecode(input, output string) error {
in, _ := os.Open(input)
out, _ := os.Create(output)
defer in.Close()
defer out.Close()
decoder := base64.NewDecoder(base64.StdEncoding, in)
_, err := io.Copy(out, decoder)
return err
}
Don't forget: Always call encoder.Close() after writing with NewEncoder. The encoder buffers incomplete groups internally — Close() flushes and pads the final output.
Decoding with Error Handling
package main
import (
"encoding/base64"
"fmt"
)
func safeDecode(s string) (string, error) {
// Try standard decoding first
decoded, err := base64.StdEncoding.DecodeString(s)
if err == nil {
return string(decoded), nil
}
// Fall back to URL-safe (handles both padded and unpadded)
decoded, err = base64.RawURLEncoding.DecodeString(s)
if err != nil {
return "", fmt.Errorf("invalid Base64: %w", err)
}
return string(decoded), nil
}
Verify your Go Base64 output
Paste your encoded string from Go into base64.dev to decode and inspect it instantly.
Open base64.dev →