Skip to content
Home » How to Encode and Decode Base64 on macOS

How to Encode and Decode Base64 on macOS

macOS ships a base64 command at /usr/bin/base64, so there’s nothing to install — it’s the FreeBSD version, which differs from the GNU one on Linux in a couple of ways worth knowing. This covers encoding and decoding strings and files, plus the one gotcha that makes people think base64 is broken when it isn’t: a trailing newline that echo slips into the input. Everything below was checked on macOS 26.5.2 (build 25F84).

Encode a string

Pipe the text into base64. Use printf '%s' rather than echo, because printf '%s' sends exactly the characters you gave it and nothing else:

$ printf '%s' "user:pw" | base64
dXNlcjpwdw==

Why not echo? It appends a newline to whatever you pass it, and that newline gets encoded along with your text — the single most common reason a base64 string comes out “wrong.” That’s covered in full below; for now, printf '%s' or echo -n keeps it clean.

Decode a string

Add -d to decode. On macOS the historical flag was the capital -D, but current versions accept both, so -d works too and there’s no need to reach for a different tool for portability:

$ echo "dXNlcjpwdw==" | base64 -d
user:pw

Using echo is fine here — decoders ignore the trailing newline that echo adds after the encoded text. The newline only causes trouble on the encode side, where it becomes part of what you’re encoding.

Encode or decode a file

For files, -i names the input and -o the output, so there’s no need to pipe. Encode a file — an image, say — to a base64 text file:

base64 -i logo.png -o logo.b64      # encode a file
base64 -d -i logo.b64 -o logo.png   # decode it back

Reading a file this way, there’s no echo in the pipeline, so no stray newline to worry about — base64 encodes the file’s exact bytes.

The trailing-newline gotcha

This is the one that costs people an afternoon: an auth header is rejected, the base64 string ends in an unexpected character, and base64 gets the blame. Base64 encoded exactly what it was handed — and what it was handed had a newline on the end. Three commands that look like they encode the same string:

$ printf '%s' "user:pw" | base64
dXNlcjpwdw==

$ echo "user:pw" | base64
dXNlcjpwdwo=

$ base64 <<< "user:pw"
dXNlcjpwdwo=

user:pw is seven bytes. Only the first command encodes seven of them; echo and the <<< here-string each append a newline, so the other two encode eight. The padding is the tell: base64 packs three input bytes into four output characters, so seven bytes leaves a remainder that gets padded with =, while eight bytes divides more evenly and the extra byte surfaces as those different trailing characters. This isn't a macOS quirk — echo and <<< behave the same on Linux and every other Unix shell. The fix is to feed base64 the exact bytes:

printf '%s' "$credentials" | base64

base64 does print a newline of its own after its output, but that one is harmless: if you capture the result with $(...), the shell strips it, and decoders ignore it. Only the newline on the input side changes the encoded value.

Line wrapping, and how macOS differs from Linux

A common piece of Linux advice is to add -w 0 to stop base64 from wrapping long output into 76-character lines. On macOS you don't need it, because the FreeBSD base64 doesn't wrap by default — it emits one unbroken line no matter how long the input:

$ printf 'x%.0s' {1..200} | base64 | wc -l
       1

The man base64 page confirms it: the -b count option "insert[s] line breaks every count characters. The default is 0, which generates an unbroken stream." If you want wrapping, -b sets the width, and -w is accepted too as a GNU-compatibility alias — so both of these work on macOS 26.5.2 despite -w not appearing in the short usage summary:

printf '%s' "the quick brown fox" | base64 -b 20   # wrap at 20 chars
printf '%s' "the quick brown fox" | base64 -w 20   # same, GNU-style flag

The tool that does wrap by default is OpenSSL: openssl base64 breaks output every 64 characters, and its -A flag turns that off. So if you're capturing OpenSSL's base64 and getting multi-line output, -A is the fix — but with plain base64 there's no wrapping to undo in the first place.

Make it a reusable command

It's tempting to save a shortcut as an alias, but an alias is plain text substitution — it can't take an argument, so your input ends up appended after base64 where it's read as a filename and errors out. Use shell functions instead, added to ~/.zshrc:

b64encode() { printf '%s' "$1" | base64; }
b64decode() { printf '%s' "$1" | base64 -d; }

After source ~/.zshrc, b64encode "user:pw" returns dXNlcjpwdw== — and because the function uses printf '%s', it sidesteps the newline gotcha every time. That's the whole toolkit: printf '%s' | base64 to encode, base64 -d to decode, -i/-o for files, and OpenSSL's -A only when you're wrangling OpenSSL's own wrapping. If you're on an older macOS, man base64 is the reference to confirm the -w and -d aliases against your version.

Leave a Reply

Your email address will not be published. Required fields are marked *