opscape

Text processing

base64

Encode binary data as base64 text, or decode base64 back to the original bytes with -d.

base64 [OPTION...] [FILE]

11 of 11 examples run in Debian 13.7 on 2026-10-31 · coreutils 9.7-3 · manual: base64(1)

Base64 represents arbitrary bytes using only 64 safe characters (A-Z, a-z, 0-9, +, /) plus = for padding. Every 3 input bytes become 4 output characters, so the encoded form is about a third larger. It exists to carry binary data through channels built for text: email attachments, JSON fields, HTTP headers, data URLs, Kubernetes secrets, keys pasted into a terminal.

base64 FILE encodes, base64 -d decodes. GNU base64 wraps encoded output at 76 characters per line; -w 0 turns wrapping off, which is what you want inside a variable, a header or JSON.

Base64 is an encoding, not encryption. Anyone can decode it, so "base64-encoded" secrets are exactly as secret as plain text.

Examples

Encode a string

printf 'hello' | base64
explain
output
aGVsbG8=

Five bytes become aGVsbG8=; the = pads the last group of 4 characters because 5 is not a multiple of 3.

Trap: echo adds a newline that gets encoded

echo 'hello' | base64
printf 'hello' | base64
output
aGVsbG8K
aGVsbG8=

echo appends a newline, so the first command encodes 6 bytes and gives aGVsbG8K, where the trailing K carries the newline. For passwords and tokens use printf (or echo -n) so the encoded value contains only what you meant.

Decode base64

echo 'aGVsbG8sIHdvcmxkCg==' | base64 -d
explain
output
hello, world

-d turns the text back into the original bytes: hello, world and a newline.

Encode a binary file

base64 photos/IMG_2041.png | head -n 3
explain
output
iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAIAAAD+Tyo8AAACDklEQVR42u3TQQkAAAwDseqs4sma
h/0GgSg4uLQDPBUJwMCAgQEDg4EBAwMGBgwMBgYMDBgYDAwYGDAwYGAwMGBgwMCAgcHAgIEBA4OB
AQMDBgYMDAYGDAwYGDAwGBgwMGBgMDBgYMDAgIHBwICBAQODgVUAAwMGBgwMBgYMDBgYMDAYGDAw

The PNG becomes plain text, wrapped at 76 characters per line. Every PNG starts with iVBORw0KGgo, the base64 form of its signature bytes.

One long line without wrapping

base64 -w 0 todo.txt; echo
explain
output
YnV5IG1pbGsKZml4IGxvZ2luIGJ1Zwp3cml0ZSB0ZXN0cwpmaXggbG9nb3V0IGJ1ZwpkZXBsb3kgdjIuNAo=

-w 0 disables line wrapping, so the whole file is a single line that fits into a variable, a JSON value or an HTTP header. base64 does not add a final newline in this mode, hence the echo.

Encode and decode a file without loss

base64 photos/IMG_2041.png > image.b64
base64 -d image.b64 > copy.png
cmp photos/IMG_2041.png copy.png && echo identical
wc -c photos/IMG_2041.png image.b64
output
identical
 583 photos/IMG_2041.png
 791 image.b64
1374 total

Decoding restores the file byte for byte. The text form is 791 bytes for a 583-byte image: a third larger, plus the newlines from wrapping.

Build an HTTP Basic auth header

token=$(printf 'alice:s3cret' | base64)
curl -s -H "Authorization: Basic $token" http://localhost:8080/private/report.txt | head -n 3
output
Quarterly report: confidential

HTTP Basic authentication sends user:password base64-encoded. The test server accepts the header and returns the report. (curl -u alice:s3cret does the same encoding for you.)

Make a data URL for HTML or CSS

printf 'data:image/png;base64,%s\n' "$(base64 -w 0 photos/IMG_2041.png)" | cut -c 1-60
explain
output
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAIAAA

A data URL embeds a small file directly in a web page. The output is cut to 60 characters here; the real value is one long line.

Invalid input

echo 'aGVs!bG8K' | base64 -d
explain
outputexit 1
helbase64: invalid input

! is not a base64 character. GNU base64 decodes up to that point (hel), then stops with invalid input and status 1.

Skip characters that are not base64

echo 'aGVs!bG8K' | base64 -d -i
explain
output
hello

-i ignores non-alphabet characters, so the stray ! is skipped and hello decodes completely.

The URL-safe alphabet

printf '??>>' | base64
printf '??>>' | basenc --base64url
output
Pz8+Pg==
Pz8-Pg==

Standard base64 uses + and /, which have meaning in URLs. The URL-safe variant used by JWTs replaces them with - and _. base64 has no option for it, but its coreutils sibling basenc --base64url does.

Common pitfalls

  • echo secret | base64 encodes the trailing newline too. Use printf '%s' secret | base64.

  • Wrapped output (76 characters per line) breaks headers, environment variables and JSON. Use -w 0 when the value must be one line.

  • Base64 is not encryption. Kubernetes secrets and similar "encoded" values are readable by anyone with base64 -d.

  • JWTs and many APIs use URL-safe base64 without padding. Standard base64 -d rejects the - and _ characters; translate them with tr '_-' '/+' or use basenc --base64url -d.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • Older macOS versions decode with base64 -D (capital D); recent ones accept -d as well. macOS does not wrap lines by default, while GNU wraps at 76 characters. Its -b option sets the wrap width instead of GNU's -w.

Options worth knowing

A selection, described in our own words. For the full list, see base64(1) or base64 --help.

OptionWhat it does
-d, --decodeDecode base64 input back to bytes.
-w, --wrap COLSWrap encoded lines after COLS characters; 0 disables wrapping.
-i, --ignore-garbageWhen decoding, skip characters outside the base64 alphabet.

Related