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 →