Base64 Encode Decode Cheatsheet: Windows, Linux, Mac Command Examples

Encode and decode Base64 on Windows, Linux, and Mac.

Page content

This cheatsheet shows how to encode and decode Base64 on Windows, Linux, and macOS with certutil, PowerShell, and base64, including the flags that differ by system.

bearded man is doing some advanced coding at night

This guide is part of Developer Tools: The Complete Guide to Modern Development Workflows. Base64 maps every three input bytes to four characters from A–Z, a–z, 0–9, +, and /. Decoding needs no key: the alphabet and the padding character = are enough to recover the original bytes.

Linux base64 wraps at 76 characters unless -w 0 is set, macOS base64 writes one line unless -b sets a break, and Windows certutil -encode adds certificate banners around wrapped lines. The Linux commands below were run with GNU coreutils 9.7, uutils coreutils 0.10.0, BusyBox 1.37.0, and OpenSSL 3.5.5. The macOS commands use the -i, -D, and -b flags documented for that binary since OS X 10.7.

Base64 Encode Decode on Windows

Windows does not ship a base64 command. Certutil and PowerShell are built in.

Using Command Prompt with Certutil

  1. Open Command Prompt.

  2. Encode a file to Base64:

    certutil -encode input.bin output.b64
    

    Replace input.bin with your file path and output.b64 with the desired output filename. The file starts with -----BEGIN CERTIFICATE-----, then wrapped Base64, then -----END CERTIFICATE-----.

  3. Strip the banner lines and print the body:

    certutil -encode input.bin tmp.b64 && findstr /v /c:- tmp.b64 && del tmp.b64
    

    findstr /v /c:- drops every line that contains -, which removes the two banners. The body stays wrapped. PowerShell [Convert]::ToBase64String is the one-line result.

Decode Base64 on Windows (Certutil)

certutil -decode encoded.b64 decoded.bin

certutil -decode accepts the banner file that -encode wrote. It also accepts a raw Base64 file.

Using PowerShell to Encode and Decode

PowerShell [Convert]::ToBase64String writes one line and does not add banners. For more shell automation, see the PowerShell Cheatsheet.

Encode a file:

[Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\path\to\file.bin"))

Encode a string (UTF-8):

[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("Hello, World!"))
# Output: SGVsbG8sIFdvcmxkIQ==

Encoding.Unicode is UTF-16LE. The same characters then encode as SABlAGwAbABvACwAIABXAG8AcgBsAGQAIQA=. Linux base64 of the UTF-8 bytes produces SGVsbG8sIFdvcmxkIQ==.

Decode Base64 to string:

[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ=="))
# Output: Hello, World!

Decode Base64 to file:

[System.IO.File]::WriteAllBytes("decoded.bin", [Convert]::FromBase64String((Get-Content encoded.b64 -Raw)))

FromBase64String accepts spaces, tabs, and newlines. It throws on the BEGIN CERTIFICATE banner, so a certutil file has to go through certutil -decode (or the banners have to be removed first).

Base64 Encode Decode on Linux

base64 --version names the binary on PATH. A line that starts with base64 (GNU coreutils) is GNU. On Ubuntu 25.10 and 26.04, /usr/bin/base64 is uutils from rust-coreutils; the 26.04 package checked here was uutils coreutils 0.10.0. GNU coreutils 9.7 is gnubase64 from the gnu-coreutils package.

Both accept a positional file, -d / --decode, -i / --ignore-garbage, and -w / --wrap. The default wrap is 76. -w 0 disables it. uutils 0.10.0 also treats -D as an alias of -d. GNU coreutils 9.7 exits with invalid option -- 'D' on -D.

Piping and quoting for these one-liners are covered in the Bash Cheat Sheet.

Encode a File to Base64

base64 input_file > output_file

Example:

base64 myfile.txt > encoded.txt

To print encoded content to the terminal:

base64 myfile.txt

Encode a String to Base64

printf '%s' 'Hello, World!' | base64 -w 0
# Output: SGVsbG8sIFdvcmxkIQ==

printf '%s' writes the bytes you pass and stops. echo without -n appends a newline, so the same text encodes as SGVsbG8sIFdvcmxkIQo=. In bash, echo -n "Hello, World!" | base64 -w 0 matches the printf line. echo -n is not portable to every sh.

Disable Line Wrapping (-w 0)

GNU and uutils wrap at 76 characters, including when stdout is a file or a pipe. JSON, environment variables, URLs, and a Kubernetes data value need one line. Terraform’s base64encode() returns one line of standard Base64, so CLI output that is compared with it has to be produced with wrap disabled:

base64 -w 0 input_file > output_file

Decode Base64 on Linux

base64 -d encoded_file > decoded_file
# or
base64 --decode encoded_file > decoded_file

Example:

printf '%s' 'SGVsbG8sIFdvcmxkIQ==' | base64 -d
# Output: Hello, World!

GNU coreutils since 9.5, and uutils 0.10.0, decode standard Base64 that omits = padding. printf '%s' 'SGVsbG8' | base64 -d prints Hello. Encoders still emit the padding. Older GNU base64 rejected unpadded input.

-i / --ignore-garbage skips characters outside the alphabet while decoding. Newlines are already allowed. Spaces are not, unless -i is set:

printf '%s' 'SGVs bG8=' | base64 -di
# Output: Hello

On GNU and uutils, -i does not take a filename. base64 -i input_file encodes input_file because the name is a positional argument and -i only changes decode. On macOS, -i is the input-file flag.

OpenSSL

OpenSSL 3.5.5 wraps at 64 characters. -A keeps the output on one line:

printf '%s' 'data' | openssl base64 -e -A
# Output: ZGF0YQ==
printf '%s' 'ZGF0YQ==' | openssl base64 -d -A
# Output: data

Without -A, a long buffer is broken every 64 characters, which is a different width from GNU base64.

BusyBox

BusyBox 1.37.0 accepts -d and -w COL (default 76, 0 disables wrap). --decode fails with unrecognized option '--decode', and -D fails with invalid option -- 'D'. Decoding already skips characters outside the alphabet: printf '%s' 'SGVs!bG8=' | busybox base64 -d prints Hello. -i is accepted and does not appear in the help line.

Base64 Encode Decode on macOS

macOS ships a BSD base64. A filename is passed with -i. Stdin redirection works without -i: base64 < input_file.

Encode a File to Base64

base64 -i input_file > output_file

Example:

base64 -i myfile.txt > encoded.txt

To print to the terminal:

base64 -i myfile.txt

The default break interval is 0, so the encoded text is one line. -b count inserts a newline every count characters. base64 -w 0 is the Linux option; the macOS binary does not use it. base64 -b 0 -i input_file is the explicit one-line form.

Encode and Copy to Clipboard

base64 -i input_file | pbcopy

Decode Base64 on Mac

base64 -D -i encoded_file > decoded_file

-D is decode. These -i and -D flags have been the macOS interface since OS X 10.7. A Linux script that calls base64 -d should call base64 -D on macOS.

Flags by Implementation

Flag GNU coreutils 9.7 uutils 0.10.0 BusyBox 1.37.0 macOS
-d / --decode Decode Decode (-D is an alias) -d only; --decode errors Use -D
-i --ignore-garbage while decoding --ignore-garbage while decoding Accepted, not listed in --help; non-alphabet bytes are already skipped Input file
-w N Wrap at N columns (default 76); 0 disables Same default and 0 Same; no long option Not this flag; use -b
-b N Not accepted Not accepted Not accepted Newline every N characters (default 0)
Help text alphabet RFC 4648 RFC 3548 (no RFC line) RFC 4648

RFC 4648 obsoletes RFC 3548. The standard alphabet in both documents is A–Z, a–z, 0–9, +, /.

base64 -i file encodes on Linux, because -i is a boolean there and file is the positional operand. The same command on macOS also encodes, because -i consumes file as the input path. base64 -D -i file decodes on macOS and on uutils 0.10.0.

Base64url for JWT

JWT header and payload segments use base64url (RFC 4648 section 5): - and _ replace + and /, and the = padding is usually omitted. Standard base64 -d then fails. On uutils 0.10.0 the error is invalid input.

GNU basenc (and gnubasenc on Ubuntu 26.04) accepts the unpadded form:

printf '%s' 'aGVsbG8_Ky93b3JsZA' | gnubasenc --base64url -d
# Output: hello?+/world

uutils 0.10.0 basenc --base64url -d requires padding restored to a multiple of 4. The same segment with == appended decodes. Encode and strip padding for a JWT-style token:

printf '%s' 'hello?+/world' | basenc --base64url -w 0 | tr -d '='
# Output: aGVsbG8_Ky93b3JsZA

jq decodes JWT segments with @base64d, which is the path in Decode and print JWT token.

PowerShell can build the same alphabet:

$b = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("hello?+/world"))
$b.TrimEnd("=").Replace("+","-").Replace("/","_")

Common Use Cases

Use case Command / approach
JWT header/payload base64url, often unpadded. GNU basenc --base64url -d, or jq @base64d
HTTP Basic Auth printf '%s' 'user:pass' | base64 -w 0 → Authorization: Basic dXNlcjpwYXNz
Kubernetes secret from a file kubectl create secret generic mysecret --from-file=key=file.bin
Kubernetes secret from a string kubectl create secret generic mysecret --from-literal=key=value
cURL Basic Auth curl -u user:pass, or -H "Authorization: Basic $(printf '%s' 'user:pass' | base64 -w 0)"
Data URI in HTML data:image/png;base64,$(base64 -w 0 image.png) on Linux

kubectl create secret base64-encodes --from-literal and --from-file values itself. Prefixing the value with base64 -w 0, as in --from-literal=key=$(base64 -w 0 file.bin), stores a second encoding, and a later base64 -d still returns Base64. The Kubernetes cheatsheet uses the plaintext form, --from-literal=username=admin.

In a manifest, each data value is already one line of standard Base64, and stringData holds the raw string. The API server merges stringData into data. Kubernetes documents that stringData does not work well with server-side apply. The object is encoded text; etcd stores it unencrypted unless encryption at rest is turned on.

curl -u writes the Authorization: Basic header. Building that header from user:pass is covered in the cURL Cheatsheet.

When Decode Fails

base64 --version is the check when -D or -w misbehaves. The GNU line and the uutils line are different, and -D on Linux matches the uutils line.

A decoded value that ends with an empty line includes a newline from echo. Re-encode that input with printf '%s'.

base64 file | wc -l prints how many wrapped lines the encoder emitted. A count above 1 on Linux means the command was run without -w 0.

invalid input on text that contains - or _ is a base64url alphabet, typical of a JWT segment. Decode it with basenc --base64url, and on uutils 0.10.0 append = until the length is a multiple of 4.

Subscribe

Get new posts on AI systems, Infrastructure, and AI engineering.