Base64 encode/decode · Guide

Base64 Encode a String: From Text to Base64 and Back

Encoding a string is the most common Base64 task and the one with the most language-specific traps. The encoding itself is trivial; converting text to bytes correctly is where implementations diverge.

Text is not bytes

Base64 encodes bytes. A string must first be converted to bytes using a character encoding, and that choice changes the output. "café" as UTF-8 is 5 bytes and encodes to Y2Fmw6k=; as Latin-1 it is 4 bytes and encodes to Y2Ff6Q==.

Use UTF-8 unless something forces otherwise. It is what the web, JSON, and virtually every modern runtime assume.

In each language

Shell
# shell — printf, not echo (echo appends a newline)
printf %s 'hello' | base64
printf %s 'aGVsbG8=' | base64 -d

# PHP
base64_encode('hello');
base64_decode('aGVsbG8=');

# Python — bytes in, bytes out
import base64
base64.b64encode('hello'.encode()).decode()
base64.b64decode('aGVsbG8=').decode()

Python is strict about the boundary: b64encode takes bytes and returns bytes, so you encode and decode around it. That verbosity is what prevents the silent encoding bugs other languages allow.

The JavaScript Unicode trap

btoa() throws InvalidCharacterError on any character above U+00FF, so btoa("café") fails outright. It operates on Latin-1, not Unicode.

Convert to UTF-8 bytes first:

JavaScript
const encode = (str) =>
  btoa(String.fromCharCode(...new TextEncoder().encode(str)));

const decode = (b64) =>
  new TextDecoder().decode(
    Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))
  );

The old unescape(encodeURIComponent(str)) trick does the same job but relies on a deprecated function. Prefer TextEncoder.

Decoding back

Decoding is lossless — you get exactly the bytes that went in. Whether those bytes are readable text depends on what was encoded; an image or a gzip stream decodes fine and displays as noise.

If a decode fails, check the length is a multiple of 4 after trimming, and look for - or _, which indicate base64url rather than the standard alphabet.

Frequently asked questions

Why does btoa throw on my string?

It only accepts characters up to U+00FF. Convert to UTF-8 bytes with TextEncoder first.

Does encoding a string change its length?

Yes, the output is about 33% longer than the byte length of the input.

Why does my decoded string show question marks?

The bytes were decoded with the wrong character encoding, or the original was not text at all.

Ready to try it?

Open the free browser-based Base64 encode/decode and apply what you just read — no sign-up, runs locally.

Open the Base64 encode/decode tool