Decode Base64 in the Terminal, JavaScript and Python
Decoding Base64 is one call in every language, and each call has a trap: atob garbles accents, Buffer accepts junk, and Python rejects missing padding. Here is the correct version of each.
To decode Base64 on the command line, in JavaScript or in Python, you need one call, but each one has a trap that produces wrong output without an error. The Base64 Decode tool decodes in the browser and its page explains how to recognise Base64 and why decoded bytes are not always text. This guide covers decoding in your own terminal and code.
Decode Base64 on the command line
echo 'w6k=' | base64 -d # macOS and Linux
echo 'w6k=' | openssl base64 -d -A
base64 -d secret.b64 > secret.bin # decode a fileBoth commands print é. -A tells openssl that the input is one line, which matters for long Base64 strings without line breaks. Redirect binary output to a file instead of printing it, because raw bytes can garble the terminal.
Decode Base64 in JavaScript
atob is built into browsers and Node, but it returns a binary string: one character per byte. For anything outside ASCII, that gives the wrong text:
atob("w6k=");
// "é" wrong: the two UTF-8 bytes of é shown as two characters
new TextDecoder().decode(Uint8Array.from(atob("w6k="), (c) => c.charCodeAt(0)));
// "é" correct in browsers and Node
Buffer.from("w6k=", "base64").toString("utf8");
// "é" correct in Node
Buffer.from(token, "base64url").toString("utf8");
// Node also decodes the URL-safe alphabet directlyatob tolerates spaces, line breaks and missing padding, but rejects the URL-safe characters - and _ with an InvalidCharacterError. Replace them with + and / first. Buffer goes the other way and accepts almost anything: invalid characters are skipped silently, so Buffer.from("w6k-!!", "base64") decoded to é> without complaint. Validate input you did not produce yourself.
Decode Base64 in Python
import base64
base64.b64decode("w6k=").decode("utf-8") # 'é'
def b64url_decode(text):
return base64.urlsafe_b64decode(text + "=" * (-len(text) % 4))
b64url_decode("eyJzdWIiOiJ1XzQyIn0") # b'{"sub":"u_42"}'
base64.b64decode(text, validate=True) # reject anything outside the alphabet- Python insists on padding. JWT segments and other URL-safe values usually drop the
=, and decoding them directly fails withIncorrect padding. The helper above adds the missing padding. - By default
b64decodeskips characters outside the alphabet.validate=TrueraisesOnly base64 data is allowedinstead, which is what you want for input from outside your system. - The result is bytes. Call
.decode("utf-8")only when you know the content is text.
Related tools
Related guides
Base64 turns bytes into plain text so they can travel where only text is allowed. Here are the five places developers meet it most, with real examples, and the cases where it is the wrong tool.
Tracking links and login redirects nest one URL inside another, encoded once per layer. Here is how to unwrap them correctly in JavaScript and Python, and the redirect check that keeps it safe.
Decoding a JWT shows its claims; verifying it proves they are genuine. Here are the checks a correct verification makes, written out in Node.js and Python so you can see each step.