Base64 to JSON
Decode the blob and read what is actually inside it
Base64 Input
Decoded JSON
The blob you were handed
Something has given you a wall of letters and digits and you need to know what is in it. Maybe it is the middle segment of a token from a failing request. Maybe it came out of kubectl get secret -o yaml and you want to check which database it points at. Maybe a colleague pasted it into a ticket with the words "this looks wrong" and nothing else. Base64 is not encryption and it is not a checksum — it is a way of writing arbitrary bytes using 64 characters that survive being put in a URL, a header, or an XML attribute without anything mangling them.
Paste it on the left and the decoded JSON appears on the right, indented. Nothing is uploaded; the decoding happens in the page you are looking at, which matters more here than on most pages, because the things people decode are tokens and secrets.
The reason a dedicated page is worth having is that "base64" is not one thing. RFC 4648 defines two alphabets: the standard one ending in + and /, and a URL-safe one that swaps those for - and _ so the string can live in a query parameter. JWTs use the second and drop the trailing = padding entirely. The browser's own atob() rejects both of those. Hit that once and you conclude your token is corrupt, when it is only in a dialect the built-in function does not speak.
So this page repairs first and asks questions later. URL-safe characters get translated, missing padding gets restored, the line breaks that openssl base64 and MIME insert every 76 characters get stripped, and a data:application/json;base64, prefix gets removed if you pasted the whole URI. Anything it had to change is listed under the output, because "it was URL-safe" is often the fact you were missing.
Decoding a payload
- Paste the encoded string – A JWT payload segment, one line from a Kubernetes manifest, a whole data URI, or a wrapped block from
openssl— all four work as pasted. Sample loads a real unpadded token payload if you want to see the shape first. - Read the JSON on the right – Valid JSON is indented two spaces. If the payload turns out not to be JSON, you still get the decoded text, with a note saying what it is not — that is usually more useful than an error, because you now know what you are holding.
- Check the note under the output – It says what had to be repaired. Seeing "it used the URL-safe alphabet" tells you the string came out of a URL or a token rather than a config file, which is often the thing you were trying to establish.
- Take it somewhere useful – Copy it, or send it on to JSON to Table if the payload has an array you want to read as a grid, or JSON Tree Viewer if it is deeply nested.
If you are debugging a token, decode only the middle segment — the part between the two dots. The first segment is the header and decodes fine, but the third is a signature over raw bytes and will never be text. Getting a "not valid UTF-8" message from that third segment is the tool working correctly, not failing.
A token payload, and the number nobody notices
This is a real unpadded, URL-safe-style segment — 211 characters, which is not a multiple of four, so atob() alone refuses it. Watch iccid: it is 19 digits, and if you decode this in a browser console with JSON.parse you get 8901240544102066000. The last three digits are gone, silently.
eyJzdWIiOiJTVUItMTAwMSIsIm1zaXNk biI6IjQ0NzcwMDkwMDE0MiIsImljY2lk Ijo4OTAxMjQwNTQ0MTAyMDY2MjQ2LCJw bGFuIjoiVW5saW1pdGVkIDVHIiwicm9h bWluZyI6dHJ1ZX0 # no padding, and it would use - and _ # if the payload contained them
{
"sub": "SUB-1001",
"msisdn": "447700900142",
"iccid": 8901240544102066246,
"plan": "Unlimited 5G",
"roaming": true
}When you reach for this
Working out why a token is being rejected
Decode the payload and look at exp, aud and iss. Nine times out of ten the answer is an expiry in the past or an audience pointing at the staging API, and both are visible in about four seconds once the payload is readable. Do keep in mind that decoding proves nothing about validity — the signature is what makes a token trustworthy, and it is not checked here or by any decoder.
Reading a Kubernetes secret
Every value under data: in a Secret is Base64, which is encoding rather than protection — the Kubernetes documentation says so plainly, and it still surprises people. If the value is a JSON service-account key or a connection config, this page shows it properly indented.
Opening a data URI from a stylesheet or a page source
Paste the whole thing, data:application/json;base64, prefix and all. The prefix is recognised and stripped, and the declared media type is reported back so you can see whether it matches what actually came out.
Checking what an API really sent
Some APIs return a Base64 body for anything that might contain characters awkward to transport — cursors, saved filters, exported settings. Decoding it tells you whether the bug is in what the server produced or in what your client did with it afterwards.
What it handles
- URL-safe input, unpadded. A JWT segment with
-,_and no trailing=decodes as pasted — the case the browser's ownatob()throws on. - Wrapped and prefixed input. Line breaks from MIME or
openssl base64are stripped, and adata:URI prefix is removed and reported. - Long integers survive. A 19-digit ICCID or snowflake ID comes out with every digit — most decoders hand it to
JSON.parse, which rounds it without a word. - Non-JSON payloads still show. If the bytes are plain text, a certificate body or a log line, you see them, with a note rather than a dead end.
- Binary is identified, not garbled. Decode a PNG or a gzip archive by mistake and the page tells you what it looks like and how many bytes it is, instead of printing a screen of replacement characters.
- Nothing is transmitted. The decode happens in this tab, which is the only acceptable answer for a page people paste production tokens into.
Questions that come up
Why does my string work here but not with atob()?
Almost always one of two things. Either it uses the URL-safe alphabet, where - and _ stand in for + and /, or its padding was stripped so the length is not a multiple of four. atob() handles neither. It is worth knowing the reverse trap too: btoa() does not throw on café, it quietly encodes it as Latin-1 and produces Y2Fm6Q== where UTF-8 wants Y2Fmw6k=. MDN's write-up covers the whole mess.
Is Base64 a form of encryption?
No, and treating it as one is how secrets end up in public repositories. It is a reversible transcoding with no key involved — anyone holding the string can read it, exactly as you just did. Its job is transport: getting bytes through a channel that only tolerates text.
Why is my payload about a third bigger than the original?
Base64 spends 4 output characters on every 3 input bytes, so the size grows by roughly 33% plus padding. That is the cost of the alphabet, and it is why an image embedded as a data URI is always larger than the file on disk.
It says the payload is not valid UTF-8. What does that mean?
The Base64 was fine — it decoded to bytes — but those bytes are not text. That is what a JWT signature segment, an image, or a gzip archive looks like from here. The page names the likely file type where it recognises the leading bytes, so you can tell a PNG from a certificate without going near a hex editor.
Can I go the other way?
JSON to Base64 encodes, with a choice of standard, URL-safe or MIME-wrapped output. If your payload turns out to be XML or YAML rather than JSON, Base64 to XML and Base64 to YAML check those instead.
What about a really long number in the JSON?
It stays exact. JavaScript numbers hold about 15 reliable digits, so anything longer is normally rounded the moment it is parsed — the mechanism is explained in the piece on large numbers in JSON. This page reads the digits out of the source text rather than trusting the parsed value, so a 19-digit identifier arrives intact and stays a number rather than being quoted into a string.
Related tools
Worth reading
- RFC 4648 §5 — the URL and filename safe alphabet – Two pages, and it explains every base64 dialect you will meet
- MDN: data URIs – The syntax of the prefix this page strips, and when embedding is worth the 33%
- RFC 8259 — the JSON specification – Notably silent on how big a number may be, which is where the rounding problem starts