How to Encode and Decode Base64: Online, Command Line, JavaScript, Python and Files

Base64 turns bytes into 64 safe ASCII characters so binary data can travel through text-only channels: JSON, email, HTTP headers, data URIs, environment variables. Encoding and decoding take one call on every platform. Getting back exactly what went in depends on handling three things correctly: which bytes represent your text, which Base64 alphabet was used, and whether line breaks or padding were added along the way.

Encode and decode online

Paste text to encode it, or paste a Base64 string to decode it. A good decoder tells you whether the result is readable UTF-8 text or binary data, such as an image, a PDF or a compressed file, and lets you download binary output instead of showing it as garbage characters.

Base64 is often searched for as "base64 decrypt", but there is nothing to decrypt. Base64 has no key: anyone can decode it, which is also why it must never be used to hide passwords or tokens. For the same reason, use a decoder that works in the browser without uploading the input when the string might contain credentials, such as an Authorization header or a Kubernetes secret.

text     : Hello, DevToolGrid!
base64   : SGVsbG8sIERldlRvb2xHcmlkIQ==

text     : café
base64   : Y2Fmw6k=          (UTF-8: é is two bytes, C3 A9)

On the command line

# Linux (GNU coreutils)
printf '%s' 'Hello' | base64            # SGVsbG8=
printf '%s' 'SGVsbG8=' | base64 -d      # Hello
base64 -w0 photo.jpg > photo.b64        # -w0: no line wrapping
base64 -d photo.b64 > photo.jpg

# macOS (BSD base64; older releases use -D to decode)
printf '%s' 'Hello' | base64
base64 -d -i photo.b64 -o photo.jpg

# OpenSSL, anywhere
openssl base64 -A -in photo.jpg         # -A: single line
openssl base64 -d -A -in photo.b64 -out photo.jpg
# Windows PowerShell
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('Hello'))
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('SGVsbG8='))
[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\photo.jpg'))

# Windows cmd (certutil adds -----BEGIN/END----- lines)
certutil -encode photo.jpg photo.b64
certutil -decode photo.b64 photo.jpg

Two command-line traps cause most bad output. echo adds a newline, so echo Hello | base64 gives SGVsbG8K, the encoding of Hello\n. Use printf '%s'. And GNU base64 wraps its output at 76 characters, which breaks data URIs, JSON values and environment variables; add -w0.

In JavaScript: browser and Node.js

btoa() and atob() are older than Unicode support on the web. They treat each character as one byte and throw InvalidCharacterError for anything outside Latin-1, which includes most emoji and many letters. Convert text to UTF-8 bytes first.

// Browser: UTF-8 safe
const encode = (text) =>
  btoa(String.fromCharCode(...new TextEncoder().encode(text)));
const decode = (b64) =>
  new TextDecoder().decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));

encode('café 😀');                 // 'Y2Fmw6kg8J+YgA=='
decode('Y2Fmw6kg8J+YgA==');        // 'café 😀'

// Newer browsers (Baseline since September 2025): direct byte methods
new TextEncoder().encode('café').toBase64();       // 'Y2Fmw6k='
Uint8Array.fromBase64('Y2Fmw6k=', { alphabet: 'base64' });
// Node.js: Buffer handles UTF-8 and binary
Buffer.from('café 😀', 'utf8').toString('base64');     // 'Y2Fmw6kg8J+YgA=='
Buffer.from('Y2Fmw6kg8J+YgA==', 'base64').toString('utf8');
Buffer.from(token, 'base64url');                       // URL-safe variant
require('fs').readFileSync('photo.jpg').toString('base64');

The spread in String.fromCharCode(...bytes) hits the engine's argument limit sooner than people expect: in Node.js 24 it throws RangeError: Maximum call stack size exceeded somewhere between 120,000 and 150,000 bytes. For files, use FileReader.readAsDataURL() in the browser, or process the bytes in chunks.

In Python and PHP

import base64

base64.b64encode('café'.encode('utf-8')).decode('ascii')   # 'Y2Fmw6k='
base64.b64decode('Y2Fmw6k=').decode('utf-8')               # 'café'

base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))          # Base64url, padding restored
base64.b64decode(s, validate=True)                          # reject non-alphabet characters

with open('photo.jpg', 'rb') as f:
    data = base64.b64encode(f.read())
<?php
echo base64_encode('café');                       // Y2Fmw6k=
echo base64_decode('Y2Fmw6k=');                    // café
var_dump(base64_decode('not*base64', true));       // false: strict mode
$data = base64_encode(file_get_contents('photo.jpg'));

By default, PHP's base64_decode and Python's b64decode quietly skip characters outside the alphabet. A corrupted string therefore decodes to slightly wrong bytes instead of raising an error. Use strict mode (true in PHP, validate=True in Python) when the input comes from outside.

Why decoded output looks garbled

Base64 restores bytes exactly. It does not record what those bytes mean. If the output looks wrong, the bytes are fine and the interpretation is not.

Mojibake such as café means UTF-8 bytes were read as Latin-1 or Windows-1252: decode as UTF-8. Output starting with %PDF, PK, \x89PNG or \xFF\xD8 is a PDF, a ZIP-based file (including DOCX and XLSX), a PNG or a JPEG: save the bytes to a file instead of displaying them. Output starting with \x1F\x8B is gzip-compressed and needs decompressing after decoding. Output with a \xFE\xFF or \xFF\xFE start is UTF-16 text.

If decoding fails outright, check for the URL-safe alphabet (- and _ instead of + and /), missing = padding, whitespace or line breaks from copy-paste, a data:…;base64, prefix still attached, or + characters turned into spaces by URL decoding.

Size and padding

Base64 encodes every 3 bytes as 4 characters, so output is about 33% larger than the input. Precisely, it is 4 × ceil(n / 3) characters for n bytes. When the input length is not a multiple of 3, one or two = characters pad the last group, so the length stays a multiple of 4.

"M"    -> TQ==      (1 byte  -> 2 chars + ==)
"Ma"   -> TWE=      (2 bytes -> 3 chars + =)
"Man"  -> TWFu      (3 bytes -> 4 chars)

Some formats drop the padding (JWTs, Base64url in URLs). Most decoders need it back: add = until the length is a multiple of 4.

Where you meet Base64 day to day

Kubernetes stores Secret values Base64-encoded, and the encoding is often mistaken for protection. kubectl get secret db -o jsonpath='{.data.password}' | base64 -d reveals the value to anyone with read access, so restrict who can read Secrets and enable encryption at rest. Use stringData in manifests to write plain values and let Kubernetes encode them.

JSON has no binary type, so APIs that carry files, images or signatures put them in Base64 strings. Check the documentation for which alphabet and whether padding is included. Email attachments are Base64 inside MIME parts, wrapped at 76 characters. Environment variables and CI secrets often hold Base64-encoded certificates or service-account keys, because the raw files contain newlines that many systems handle badly.

In each of these cases the encoding exists to keep bytes intact through a text channel, not to hide them.

# Kubernetes: create a secret without encoding by hand
kubectl create secret generic db --from-literal=password='s3cr3t'

# CI: store a key file as one line, restore it in the job
base64 -w0 service-account.json            # paste output into the CI secret
printf '%s' "$SA_KEY_B64" | base64 -d > service-account.json

Sources and further reading

RFC 4648 defines Base64, Base64url and padding. RFC 2045 defines the MIME variant with 76-character lines. MDN documents btoa(), atob(), Uint8Array.fromBase64() and toBase64(). The GNU coreutils manual covers base64 -w, and the Python base64 and PHP base64_decode documentation cover strict decoding.

Frequently asked questions

How do I decode Base64?

Paste the string into a Base64 decoder, or run `printf '%s' 'SGVsbG8=' | base64 -d` on Linux or macOS. In code use `Buffer.from(s, 'base64')` in Node.js, `base64.b64decode(s)` in Python or `base64_decode($s)` in PHP. Then decode the bytes as UTF-8 if they represent text.

Can Base64 be decrypted?

Only in the sense of decoding: every "Base64 decrypt" tool is a decoder, because Base64 has no key. If the decoded bytes are still unreadable, the data was encrypted or compressed before it was Base64-encoded, and that inner layer needs its own key or tool.

Why does btoa() throw an error for my text?

`btoa()` only accepts characters in the Latin-1 range. Encode the text to UTF-8 bytes first with `TextEncoder`, then Base64-encode those bytes, or use `Uint8Array.prototype.toBase64()` where it is available.

How do I convert a file to Base64?

Read the file as raw bytes and encode them: `base64 -w0 file` on Linux, `[Convert]::ToBase64String([IO.File]::ReadAllBytes(path))` in PowerShell, or `FileReader.readAsDataURL()` in the browser. Never read a binary file as text first.

Why is my decoded Base64 text garbled?

Either the bytes use a different text encoding than the one you decoded with, or they are binary data such as an image, PDF or gzip stream. Check the first bytes for a file signature and decode text as UTF-8.

Why is Base64 output longer than the input?

Each 3 bytes become 4 characters, so the output is about 33% larger, plus padding. MIME-style encoders also add a line break every 76 characters.