Base64 Encoder & Decoder
Last reviewed on April 27, 2026.
Paste text into either box and the other one updates as you type. UTF-8 is handled correctly, so non-ASCII input round-trips without mojibake. Switch to URL-safe mode if the encoded output needs to ride in a URL or a JWT.
What Base64 actually is
Base64 is a way of writing binary data using only sixty-four printable characters: A–Z, a–z, 0–9, plus two extras (+ and / in standard, - and _ in URL-safe). The encoder takes three input bytes (24 bits), splits them into four 6-bit groups, and emits one Base64 character per group. The output is roughly 4/3 the length of the input, plus padding.
It is not encryption. It is not compression. It is a transport format — designed for systems that historically did not handle the full range of byte values cleanly, like email (SMTP) and URL parameters.
Standard vs. URL-safe
The two variants differ in only two characters and the padding rule:
| Standard | URL-safe | |
|---|---|---|
| 62nd character | + | - |
| 63rd character | / | _ |
| Padding | = required | = often omitted |
| Where it shows up | Email attachments, data: URLs, most APIs | JWTs, query parameters, OAuth state tokens |
Use URL-safe whenever the output sits in a URL, a cookie, or a header. The + and / in standard Base64 are reserved characters in URLs and would have to be percent-encoded — defeating the point of using Base64 for compactness.
UTF-8 and why it matters
JavaScript strings are UTF-16 internally. Naively passing a string to btoa() fails on any character outside Latin-1 (btoa("日本") throws). The tool above first encodes the input as UTF-8 with TextEncoder, so emoji, CJK characters, accented Latin, and everything else round-trips cleanly. Decoding reverses that — bytes out, then TextDecoder('utf-8') back to a string.
If you are decoding output from a system that does not use UTF-8 (rare, but it happens with old Windows COBOL exports or Latin-1 email bodies), the resulting bytes will appear as garbled characters. That is a charset mismatch, not a Base64 bug.
Worked example
The string Hi! encodes as follows:
- UTF-8 bytes:
0x48 0x69 0x21(binary01001000 01101001 00100001). - Split into 6-bit groups:
010010 000110 100100 100001. - Map each group to its Base64 alphabet index:
18 → S,6 → G,36 → k,33 → h. - Result:
SGkh.
No padding is needed because the input was a multiple of 3 bytes. If the input is 1 byte short of a multiple of 3, the encoder appends ==; if it is 2 bytes short, it appends =. URL-safe Base64 commonly omits the padding because the receiving end can recompute it from the length.
Common mistakes
- Treating Base64 as encryption. Anyone with a decoder (every browser console) can read it. Base64 obfuscation has been broken in seconds since 1987.
- Using standard Base64 in a URL. The
/in the output collides with the URL path separator, and many web frameworks will URL-decode+back to a space. Use the URL-safe variant for anything that travels in a URL. - Mixing the variants. A standard-encoded JWT is invalid. A URL-safe-decoded standard string fails on the
+and/characters. Match the variant to the source. - Forgetting that bytes ≠ characters. The size of an encoded string is roughly
(input_bytes × 4 / 3). For UTF-8 input, one emoji can be four bytes, so the encoded size grows accordingly. - Pasting whitespace. Many sources break encoded output across lines (MIME does this on purpose). The decoder strips whitespace, but if you are debugging by eye, remember a 76-column wrap is normal.
Where this fits with the other tools on the site
Pair Base64 with the URL encoder when you are debugging a request that double-encodes its parameters. For inspecting JSON payloads after a Base64 round-trip, send the decoded text through the JSON formatter. When you are generating prop data for a screen recording, the fake prop-data generator covers IDs and hashes; combine it with this tool when the recording shows a JWT-like token.