JaguarVPN logo

Base64 Encoder and Decoder

Both directions, Unicode-safe, with the URL-safe alphabet when you need it.

What it is for

Base64 solves one problem: you have arbitrary bytes, and a channel that will only carry printable text. Email headers, JSON string fields, URLs, HTML attributes, and a good deal of older infrastructure all fall over on raw binary. Base64 maps every three bytes onto four characters drawn from a 64-symbol alphabet that survives all of them.

That is the whole purpose. It is not compression — the output is a third larger. It is not encryption — there is no key. It is a transport encoding, and every property people mistakenly attribute to it comes from mistaking “I cannot read this” for “this is protected”.

Where you will meet it

  • JWTs. All three segments, in the URL-safe alphabet with padding stripped.
  • HTTP Basic auth. Authorization: Basic is just user:password Base64-encoded — which is exactly why Basic auth over plain HTTP is equivalent to sending the password in the clear.
  • Data URIs. data:image/png;base64,... embeds a file directly in HTML or CSS.
  • PEM certificates and keys. The lines between -----BEGIN----- and -----END----- are Base64-encoded DER.
  • Email attachments. SMTP is a text protocol; MIME uses Base64 to carry anything that is not.

The Unicode trap

The browser gives you btoa and atob, and they are a trap. Both operate on bytes represented as a string, so btoa throws on any character above U+00FF. Try it on an emoji and you get InvalidCharacterError.

The fix is to convert to UTF-8 bytes first, which is what this page does:

// Encode any string safely
const bytes = new TextEncoder().encode(text);
const b64 = btoa(String.fromCharCode(...bytes));

// Decode back
const binary = atob(b64);
const out = new TextDecoder().decode(
  Uint8Array.from(binary, c => c.charCodeAt(0))
);

One caveat on the encode line: spreading a large array into String.fromCharCode overflows the call stack somewhere around a hundred thousand elements. For anything large, loop in chunks — this page does, which is why it handles a multi-megabyte paste without falling over.

The two alphabets

Standard (RFC 4648 §4)URL-safe (§5)
Index 62+-
Index 63/_
Padding=, requiredUsually omitted
Used byMIME, PEM, Basic authJWTs, URLs, filenames

The padding is the part that causes trouble. Its only job is to make the length a multiple of four, and a decoder can always work out how much is missing — so the URL-safe variant drops it. Strict decoders then reject the result, which is why a JWT segment pasted into another tool so often fails. The decoder here restores the padding itself.

Where it is misused

Because Base64 is unreadable at a glance, it gets used as though it were a secret. It is worth naming the specific patterns:

  • Base64 as password storage. This is plaintext with a costume on. Passwords need a slow one-way hash — bcrypt, scrypt, Argon2.
  • Base64 in a JWT payload as though it were private. The payload is readable by anyone holding the token, including the user. Never put anything in it you would not show them.
  • Base64 to sneak data past a filter. It works, briefly, and every scanner worth having decodes it anyway.

The rule that covers all three: if the security of something depends on the reader not decoding it, it has no security.

Related

The JWT decoder splits a token and parses both segments in one step. For escaping text going into a URL rather than encoding bytes, percent-encoding is the different thing you probably want. And if what you decoded turns out to be JSON, the formatter will lay it out.

Base64 questions

Is Base64 encryption?

No, and this is worth being blunt about because the confusion causes real incidents. Base64 is a reversible encoding with no key and no secret — anyone who has the string has the data, and decoding it takes one line of code. It exists to move bytes through channels that only carry printable text, not to hide anything. Storing a password Base64-encoded is storing it in plaintext with an extra step.

Why do so many Base64 tools break on emoji or accented characters?

Because the browser's btoa function operates on bytes rather than characters and throws on anything above U+00FF. Tools that call it directly fail on é, on 日本語 and on every emoji. This page encodes to UTF-8 bytes first and decodes back the same way, so the round trip is clean for any text you can type.

What is URL-safe Base64?

The standard alphabet includes + and /, which both have meaning in a URL, and = padding, which does too. The URL-safe variant defined in RFC 4648 substitutes - for + and _ for /, and usually drops the padding. It is what JWTs use for all three segments, which is why a JWT segment pasted into a standard decoder often fails until the padding is restored. The decoder here accepts either alphabet, padded or not.

Why is the encoded string bigger than what I put in?

By about a third, always. Base64 represents three bytes of input as four printable characters, so the output is 4/3 the size before padding. That is the price of using only characters that survive systems which cannot carry arbitrary bytes. It is also why embedding images as data: URIs in CSS makes the stylesheet noticeably larger than the image files were.

I decoded something and got gibberish. What happened?

Most likely the bytes are not text. Base64 encodes arbitrary binary — an image, a certificate, a compressed archive — and decoding those to a string produces nonsense because there is no text there to recover. This page tells you that explicitly rather than showing you mojibake: it decodes with strict UTF-8 validation, so invalid byte sequences are reported as binary rather than silently replaced with question marks.

Last reviewed . Found something out of date? Tell us.