Base64 Decoder
Decode Base64 strings back to plain text or binary; paste a value and see the result instantly.
File Upload
About Base64 Decoding
What is Base64?
Base64 is a group of binary-to-text encoding schemes that represent binary data in an ASCII string format.
Common Uses
- • Embedding images in HTML/CSS
- • Storing binary data in JSON
- • Email attachments
- • API data transmission
Paste an encoded string and get the original bytes back. Decoding fails in more interesting ways than encoding does: implementations disagree about padding and stray characters, URL-safe input needs translating first, and the bytes that come out carry no record of what they were. This page covers those.
Decoders disagree about padding and stray characters
There is no single answer to "is this valid base64", because runtimes made different calls. The browser's atob() follows the HTML specification's forgiving-base64 algorithm: it strips ASCII whitespace, accepts input with the padding missing entirely, and fails only when the length modulo four is one or a character falls outside the alphabet. atob("SGVsbG8") returns Hello quite happily.
Python is stricter about that same string. base64.b64decode("SGVsbG8") raises binascii.Error: Incorrect padding — though by default it silently discards characters outside the alphabet, and you have to pass validate=True to make it reject them. Go's base64.StdEncoding requires padding and offers RawStdEncoding as the unpadded variant, so the decision is made once on the encoding object rather than per call. Java's Base64.getDecoder() rejects line breaks outright; wrapped input needs getMimeDecoder().
Node is the most permissive of the lot. Buffer.from(str, "base64") accepts unpadded input, accepts both alphabets, and quietly drops whatever it does not recognise — Buffer.from("SGVs!!!bG8=", "base64") returns Hello with no error at all. That is convenient while debugging and a genuine liability in validation code, because corrupted input decodes to plausible output instead of throwing.
URL-safe input has to be translated first
If a string contains - or _ and no + or /, it is base64url from RFC 4648 section 5 and a standard decoder will refuse it. atob("-__-") throws InvalidCharacterError rather than returning the three bytes fb ff fe that string represents.
The translation is mechanical: replace every - with +, every _ with /, then append = until the length reaches a multiple of four. The number to append is zero, one, or two depending on the remainder — and if the remainder is one, stop, because no valid base64 string has that length. You are holding a truncated value, and padding it out produces bytes that decode without complaint and mean nothing.
This turns up most often with JSON Web Tokens, whose three segments are unpadded base64url, and with any value that has passed through a URL path or a filename. Some libraries absorb it for you — Python has urlsafe_b64decode, Node's Buffer takes either alphabet — while the ones that do not report an alphabet error rather than a useful one. When a token segment is what you are after, the JWT decoder splits and translates all three segments in one step.
The decoded bytes do not say which encoding they are
Base64 carries bytes. It records nothing about whether those bytes are UTF-8 text, UTF-16 text, Latin-1 text, or not text at all, so interpretation is a separate decision that something in your stack is making — possibly wrongly, and always silently.
The browser makes it wrongly by default. atob() returns a binary string in which each character is one byte in the range 0–255, which is Latin-1 under another name. Decoding Y2Fmw6k= that way yields café, because the two UTF-8 bytes 0xC3 0xA9 that encode é get read as two separate Latin-1 characters. The correct sequence recovers the bytes and then decodes them explicitly: new TextDecoder().decode(Uint8Array.from(atob(str), c => c.charCodeAt(0))).
Learn the visual signatures, because they identify the bug on sight. Output littered with Ã, Â, or †is UTF-8 bytes being displayed as Latin-1, meaning the payload is intact and only the final interpretation is wrong. Output with null bytes interleaved between otherwise readable ASCII is UTF-16 being read as single-byte text. Neither case is a decoding failure, so nothing throws and no error is logged; you have to notice.
Spotting a binary payload from its first few characters
Because the mapping is positional, a fixed sequence of leading bytes always produces the same leading base64 characters. That makes file signatures readable straight off the encoded string, before you decode anything at all.
A string beginning iVBORw0KGgo is a PNG. /9j/ is a JPEG. JVBERi0 is a PDF. UEsDB is a ZIP container, which also covers .docx, .xlsx, .jar, and .apk, since every one of those is a zip archive underneath. H4sI is gzip. Recognising any of them tells you immediately that rendering the output in a text pane will produce control characters and replacement glyphs, and that what you want is a file download.
Where no signature is recognisable, length is the next clue. A decoded payload that is a whole multiple of 16 bytes with no structure and no printable runs is most likely ciphertext, since AES uses a 16-byte block and CBC-mode output is always a multiple of it. Something decoding to exactly 32 or 64 bytes of high-entropy noise is more likely a SHA-256 or SHA-512 digest than anything intended to be read.
Decoded output is untrusted input
Decoding is a transformation, not a validation. Whatever emerges deserves exactly the scrutiny you would give a raw file upload or a query parameter, and attackers rely on teams forgetting that distinction.
Base64 is a standard way to walk a payload past a filter that inspects literal strings. A rule matching <script never fires on PHNjcmlwdA, which is those same seven characters encoded. A decoded blob can equally be a decompression bomb — the well-known 42.zip is 42 KB on disk and expands to roughly 4.5 petabytes across five nested layers — or a polyglot file that is simultaneously a valid image and a valid archive, passing an image-type check and then being handled as something else entirely.
Three habits cover most of the risk. Cap the decoded size before allocating or writing anything, since the output will be about three quarters of the input length and an oversized string can be rejected without decoding it at all. Verify the actual leading bytes against the content type you were promised, rather than trusting a data: URI's own MIME label or a filename extension. And run your normal sanitisation against the decoded value rather than the encoded one, because a validator inspecting the base64 string is inspecting the wrapper and not the contents.
Related tools
Try these tools that work well with this one
Base64 Encoder
Convert text and files to Base64 format
JWT Decoder
Decode and inspect JSON Web Tokens
URL Decoder
Free online URL decoder tool for decoding percent-encoded URLs
API Tester
Send HTTP requests and view responses
CSV to JSON Converter
Convert CSV data to JSON
CSV to XML Converter
Convert CSV text to XML
Related guides
Tutorials and guides to get more from this tool
Base64 Encoding: What It Is and How It Works
Master Base64 encoding in 5 minutes! Learn when to use it, how it works, and avoid common mistakes. Essential for API developers in 2025.
JSON Web Tokens (JWT): A Complete Guide
Learn how JWTs work, how to decode them, and why they are the standard for modern authentication.
Regex Cheat Sheet: Patterns That Hold Up in Production
A practical regex reference with tested patterns for IDs, dates, URLs and logs, plus the mistakes that cause bugs and catastrophic backtracking.
ThenCatch is a free developer toolkit. Learn more about us.