Base64 encode and decode online
Input
emptyBase64
Why so many base64 tools mangle emoji
The browser has built-in base64 functions called btoa and atob, and the obvious way to write this tool is to hand them your text. That works until the text contains a character outside latin1 — anything above U+00FF — at which point btoa throws and atob does something worse: it returns the wrong string without complaining.
The reason is that neither is a text function. They map one character to one byte, so they can only handle a string where every character already is a byte. Text is not that. José 🎉 is six characters and ten UTF-8 bytes, and there is no one-to-one mapping to be had.
So this page does not pass text to either. Encoding converts the text to UTF-8 bytes first; decoding turns the bytes back into text through a strict UTF-8 decoder. José 🎉 encodes to Sm9zw6kg8J+OiQ== — or Sm9zw6kg8J-OiQ in the URL-safe alphabet — and decoding either one anywhere in the world gives José 🎉 back. A decoder that stops at atob gives you José ð instead, silently, which is the worst failure mode a decoder has.
Press Swap after encoding and you will see the original come back unchanged. That is the check worth doing on any base64 tool, including this one.
Standard or URL-safe — which one you need
Base64 needs 64 characters. Everyone agrees on A–Z, a–z and 0–9; the argument is about the last two. RFC 4648 §4 — the standard alphabet — uses + and /, and pads the end with =. RFC 4648 §5 — URL-safe — uses - and _ instead, and usually drops the padding.
The reason §5 exists is that all three of §4's extra characters mean something else in a URL: / is a path separator, + is a space in a query string, and = separates a parameter from its value. Base64 in a URL, a filename or an HTTP header therefore has to be §5, which is why JWTs and most APIs use it.
Use Standard for email attachments, data URIs, config files and Authorization: Basic. Use URL-safe for anything that will appear in a URL, a token, a cookie or a filename. The bytes underneath are identical either way — only three characters differ.
Decoding asks you nothing, on purpose. Which alphabet a string uses is visible in the string itself, so the page reads it rather than making you answer. The one case it will not resolve is input containing both - and +: that is not a variant of anything — it is two encodings glued together or a corrupted copy — and each reading decodes to different bytes, so it is reported rather than guessed.
What decoding accepts without being asked
Real base64 arrives in a state. It has been wrapped at 76 characters by a mail client, or had its padding stripped by a JWT library, or copied out of a CSS file with a data:image/png;base64, prefix still attached. Rejecting those is technically correct and practically useless, so all of them work:
- Missing padding. The trailing
=carries no data — it only rounds the length up to a multiple of four. It is added back and noted. - Line breaks, spaces and tabs. Stripped before decoding. MIME mandates a break every 76 characters and the
base64command still emits them. - A data URI prefix.
data:…;base64,is dropped and only the payload after the comma is decoded.
When the answer is "this is not text"
Base64 encodes bytes, and bytes are not always text. If you decode an image, a PDF or a private key, the result is binary and there is no sensible way to render it in an editor. The common behaviour is to run it through a lenient decoder anyway and hand back a screen of � replacement characters, which reads exactly like corrupted data and sends people looking for a fault that is not there.
This page says so instead: it reports how many bytes it decoded and that they are not valid UTF-8, so you know the base64 was fine and the content simply is not text. That is a different problem with a different answer — usually "save it to a file" rather than "re-copy the string".
Base64 is not encryption
It is worth saying plainly, because it is the most common misuse. Base64 has no key and no secret. It is a way of writing arbitrary bytes using only characters that survive systems which mangle anything else — email bodies, URLs, XML attributes. Anyone who sees your base64 can decode it in one step, on this page or any other. If the content needs to stay private, it needs encryption, and base64 is at most the thing you wrap the ciphertext in afterwards.
The encoded form is a third larger, always
Base64 spends 4 characters on every 3 bytes, because it only has 64 symbols to work with and 6 bits fit in each. That is a fixed 33% overhead — it does not depend on the content and no encoder can do better while staying base64. A 3 MB file becomes 4 MB of text. This matters more than it sounds when the result is going into a JSON payload or a database column.
Decoding a token, not a string
If what you have has two dots in it — eyJhbGci… — it is a JWT, and it is three base64url segments rather than one. You can paste each part here, but the JWT decoder splits it for you and turns exp and iat into readable dates. If the decoded result is JSON and you want to read it properly, the JSON viewer opens it as a tree.
Nothing is uploaded
Both directions run in your own browser. That is worth more than usual here, because the things people base64 are credentials — Authorization: Basic headers, service-account keys, certificates and kubectl secrets are all base64 by convention — and decoding in particular is what you do to something you were given. None of it is transmitted, stored or logged.