Working, copy-paste snippets for JavaScript, Python, PHP, Java, Go, and Bash — including the UTF-8 handling and URL-safe variants that most snippets get wrong.
Modern browsers and Node ship with atob() and btoa() for ASCII strings, and the Buffer or TextDecoder APIs for binary and UTF-8.
ASCII-only, simple cases:
const encoded = btoa("Hello, world!"); // "SGVsbG8sIHdvcmxkIQ=="
const decoded = atob("SGVsbG8sIHdvcmxkIQ=="); // "Hello, world!"
UTF-8 strings (the right way):
// Encode
const encoded = btoa(new TextEncoder()
.encode("café ☕")
.reduce((s, b) => s + String.fromCharCode(b), ""));
// Decode
const bytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
const decoded = new TextDecoder().decode(bytes); // "café ☕"
Node.js (preferred):
const encoded = Buffer.from("café ☕", "utf-8").toString("base64");
const decoded = Buffer.from(encoded, "base64").toString("utf-8");
The base64 module in the standard library covers every variant.
import base64
# Standard Base64
encoded = base64.b64encode("café ☕".encode("utf-8")).decode("ascii")
# 'Y2Fmw6kg4piV'
decoded = base64.b64decode(encoded).decode("utf-8")
# 'café ☕'
# URL-safe variant
encoded_urlsafe = base64.urlsafe_b64encode(b"\xff\xee\xdd").decode("ascii")
# '/-7d' becomes '_-7d' in URL-safe form
# Decode forgiving missing padding (common with JWTs):
def b64decode_padded(s):
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
A common stumble in Python: b64encode() returns bytes, not str. The .decode("ascii") tail converts the result to a regular string for use in JSON or logs.
<?php
$encoded = base64_encode("café ☕");
// "Y2Fmw6kg4piV"
$decoded = base64_decode($encoded);
// "café ☕"
// Strict mode (returns false on invalid input):
$decoded = base64_decode($encoded, true);
if ($decoded === false) {
throw new Exception("Invalid Base64");
}
// URL-safe — PHP doesn't have a built-in, so do it manually:
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data) {
return base64_decode(strtr($data, '-_', '+/'));
}
Use java.util.Base64 (added in Java 8).
import java.util.Base64;
import java.nio.charset.StandardCharsets;
String encoded = Base64.getEncoder()
.encodeToString("café ☕".getBytes(StandardCharsets.UTF_8));
// "Y2Fmw6kg4piV"
String decoded = new String(
Base64.getDecoder().decode(encoded),
StandardCharsets.UTF_8
);
// "café ☕"
// URL-safe variant
String urlSafe = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
byte[] back = Base64.getUrlDecoder().decode(urlSafe);
import "encoding/base64"
encoded := base64.StdEncoding.EncodeToString([]byte("café ☕"))
decoded, _ := base64.StdEncoding.DecodeString(encoded)
// URL-safe
encodedURL := base64.URLEncoding.EncodeToString(bytes)
// Without padding (for JWTs etc.)
encodedRaw := base64.RawURLEncoding.EncodeToString(bytes)
# Encode
echo -n "café ☕" | base64
# Y2Fmw6kg4piV
# Decode
echo "Y2Fmw6kg4piV" | base64 -d
# café ☕
# URL-safe variant (BSD base64 doesn't support -w 0 or URL-safe directly,
# so post-process with tr):
echo -n "$data" | base64 | tr '+/' '-_' | tr -d '='
The single biggest portability problem: text encoding. "café" is one string in your source code, but it's different bytes in UTF-8 (5 bytes) vs Latin-1 (4 bytes). Always specify UTF-8 explicitly when encoding strings — otherwise different runtimes will produce different Base64.
If you just need to decode a single string once — a JWT you're debugging, a config value, an email attachment header — opening the decoder is faster than writing the code, importing the library, and running it. Use the library when the decoding needs to happen in production; use the tool when you're investigating.
Published May 2026 · Last reviewed May 2026. Spot an error? Email contactus@base64decode.tools.