Decode a base64 string
Convert a base64-encoded string back to its original bytes — for inspecting JWT payloads, decoding kubectl secret values, reading basic-auth headers, and pulling binary out of YAML / JSON configs.
How to decode a base64 string in each shell
echo "aGVsbG8=" | base64 -d`base64 -d` (GNU) / `base64 -D` (macOS BSD — uppercase D) decodes. The `echo` includes a trailing newline that base64 silently ignores during decode (it strips whitespace by default). For a file: `base64 -d file.b64 > file.bin`. For URL-safe base64 (JWT payloads use this — `-` instead of `+`, `_` instead of `/`, no `=` padding): pre-substitute first: `echo "eyJhbGciOiJIUzI1NiJ9" | tr '_-' '/+' | base64 -d 2>/dev/null` (the `2>/dev/null` swallows the "invalid input" warning from missing padding; some implementations need explicit re-padding: `s=$(cat); pad=$(( (4 - ${#s} % 4) % 4 )); s=$s$(printf "=%.0s" $(seq 1 $pad))`).
echo "aGVsbG8=" | base64 -dmacOS BSD `base64` flags differ: `-D` (capital) for decode, `-d` is "no decode" on older versions — confusing migration trap when scripts move between Linux and macOS. `base64 --decode` is portable (long-form works on both GNU and modern BSD). For piping decoded BINARY straight to a file: `echo "$B64" | base64 -d > image.png` — but verify the source had no line wraps that confused intermediate tools.
echo "aGVsbG8=" | base64 -dSame external `base64`. Fish chaining for "decode then JSON-parse" (JWT payloads are typically JSON): `echo $jwt | string split -f 2 . | base64 -d 2>/dev/null | jq` — the `string split -f 2 .` grabs the payload (middle of `header.payload.signature`). Fish `string` builtin avoids the `cut -d. -f2` external call that bash/zsh use.
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("aGVsbG8="))Pwsh ALWAYS forces you to pick an encoding for the decoded bytes-to-string step. UTF8 is the right default for text. For raw bytes to file: `[IO.File]::WriteAllBytes("out.bin", [Convert]::FromBase64String($b64))`. URL-safe decode: pwsh has no built-in — the conventional substitution + padding: `$s = $b64.Replace("-","+").Replace("_","/"); $pad = (4 - $s.Length % 4) % 4; $s += "=" * $pad; [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s))`. `FromBase64String` THROWS on invalid input (no silent fail) — wrap in `try`/`catch` if input may be malformed.
certutil -decode infile.b64 outfile.bincmd has NO inline base64 decode — `certutil -decode` requires both input and output to be FILES (no stdin). It ALSO requires the input to start with `-----BEGIN CERTIFICATE-----` and end with `-----END CERTIFICATE-----` headers — without them, it errors "Input length = 0". The workaround: `(echo -----BEGIN CERTIFICATE-----) > tmp.b64 & type input.b64 >> tmp.b64 & (echo -----END CERTIFICATE-----) >> tmp.b64 & certutil -decode tmp.b64 out.bin`. Painfully baroque. For inline decode, shell out to pwsh: `powershell -NoProfile -Command "[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('%B64%'))"`.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **JWT payload extraction** is the most common reason to decode base64 ad-hoc. A JWT is three URL-safe-base64 sections joined by `.` — header, payload, signature. To inspect: `echo $JWT | cut -d. -f2 | base64 -d 2>/dev/null | jq` (bash). The `2>/dev/null` hides "invalid input" warnings from missing padding (URL-safe drops trailing `=`). Real-world: `kubectl get secret mysecret -o jsonpath="{.data.password}" | base64 -d` is the canonical "what's in this kube secret" one-liner.
- Padding (`=` characters at the end) is REQUIRED by standard base64 (RFC 4648 §4) and FORBIDDEN by URL-safe base64 (§5) — JWTs strip it, basic auth keeps it. Most decoders are lenient about EXTRA padding but strict about MISSING padding: GNU `base64 -d` warns on stderr but still decodes; pwsh `[Convert]::FromBase64String` throws hard. Padding rule: the encoded length must be a multiple of 4; pad with `=` to reach it. Length 11 → add 1 `=`; length 10 → add 2 `=`; length 12 → no padding needed.
- The base64 alphabet detection trick: a string with `-` or `_` characters is URL-safe (substitute back to `+` / `/` before decoding with a standard decoder); a string with `+` or `/` is standard. A string with NEITHER is ambiguous but works with either alphabet (the first 62 chars `A-Za-z0-9` are identical in both alphabets). Length divisible by 4 → likely padded standard; length NOT divisible by 4 → almost certainly URL-safe-unpadded. JWT signatures are URL-safe-unpadded by spec; kubectl secrets are standard-padded; HTTP basic auth is standard-padded.
- Common gotcha: text editors and copy-paste pipelines insert line breaks into long base64 strings, breaking decoders that don't strip whitespace. `base64 -d` strips by default (good); `[Convert]::FromBase64String` does NOT (bad) — use `.Replace("`r","").Replace("`n","").Replace(" ","")` first. For "encoded base64 was downloaded with `curl` and may have wrap": `curl ... | tr -d '\r\n ' | base64 -d` is the safe pattern.
Related commands
Related tasks
- Encode a string to base64— Convert a string (or file) to its base64 representation — for HTTP basic auth headers, JWT payloads, embedded credentials in YAML, or any text-only transport of binary.
- Format (pretty-print) JSON from a shell— Pretty-print a JSON blob from a curl response, log line, or config file — for human-readable inspection or diffing.