codr.tools
Tools

Base64 encoder / decoder

Encode text or a file to Base64 and decode it back, with correct UTF-8, standard and URL-safe alphabets, optional padding, MIME line wrapping and data: URIs.

  1. URL encoder / decoder
  2. Base64 encoder / decoder
Direction
Direction
In27 bytes
Out36 bytes
Swap
Ratio×1.33
StatusValid
Alphabet
Base64 alphabet

RFC 4648 section 4. The + and / characters have to be escaped again inside a URL.

Output

Encode text or a file to Base64 and decode it back, with correct UTF-8, standard and URL-safe alphabets, optional padding, MIME line wrapping and data: URIs.

  1. URL encoder / decoder
  2. Base64 encoder / decoder

How to use this tool#

  1. Pick a direction. Encode turns text or a file into Base64. Decode turns Base64 back. Conversion happens as you type, so there is nothing to submit.
  2. Type, paste or drop. For encoding you can type over the example, choose a file, or drag a file onto the input. Files are read in your browser and never uploaded.
  3. Set the output options. The alphabet (standard + / or URL-safe- _), whether to add = padding, whether to wrap lines at 76 characters for email, and whether to emit a data: URI.
  4. Decoding needs no options. Both alphabets are accepted automatically, padding may be missing, whitespace and line breaks are ignored, and adata:...;base64, header is stepped over.
  5. If the result is not text, the tool says so instead of showing nonsense characters. You get the byte count, a hex preview of the first bytes, and a button to save the decoded bytes as a file.

Use Swap to send the output back into the input with the direction flipped, which is the fastest way to confirm a value survives a round trip.

What Base64 is#

Base64 writes arbitrary bytes using 64 characters that survive being sent through systems built for text. It is defined in RFC 4648, and its alphabet is A-Z, a-z, 0-9, + and/, with = reserved for padding.

The trick is that 3 bytes and 4 Base64 characters are both 24 bits. The encoder takes three bytes at a time, splits those 24 bits into four groups of 6, and each group of 6 bits picks one of the 64 characters:

base64
   M         a         n          3 bytes, 24 bits
01001101  01100001  01101110

010011  010110  000101  101110    4 groups of 6 bits
    19      22       5      46    each is an index into the alphabet
     T       W       F       u    -> "TWFu"

When the input does not divide evenly by 3, the last group is short. RFC 4648 covers the two cases: one leftover byte becomes "two characters followed by two "=" padding characters", two leftover bytes become "three characters followed by one "=" padding character".

base64
"foo"    3 bytes  -> "Zm9v"      no padding needed
"fo"     2 bytes  -> "Zm8="     one "=" fills the last group
"f"      1 byte   -> "Zg=="     two "=" fill the last group

How much bigger does it get?#

Every 3 bytes become 4 characters, so encoded data is 4/3 the size of the original: an overhead of 33.3%, not an approximation but exactly one third. The precise length for n bytes is 4 × ceil(n / 3) characters with padding, andceil(4n / 3) without it.

  • A 1 KB file becomes 1,368 characters.
  • A 1 MB image becomes about 1.33 MB of text.
  • Line wrapping at 76 characters adds roughly another 1.3% in newlines.

That is why inlining large images as data: URIs is usually a bad trade: the bytes grow by a third, and unlike a separate file they cannot be cached on their own.

Standard and URL-safe alphabets#

The standard alphabet ends with + and /, and both mean something else inside a URL: / is a path separator, and + means a space in form data. Put standard Base64 in a URL and you have to percent-encode it again.

RFC 4648 section 5 solves this with one substitution. It is, in the RFC's words, "technically identical to the previous one, except for the 62:nd and 63:rd alphabet character":+ becomes - and / becomes _. Everything else, including =, is unchanged. This variant is called base64url, and the RFC is firm that it "should not be referred to as only "base64"".

Padding is usually dropped along with it, because = would itself need escaping in a URL. Section 3.2 requires padding by default and allows a specification to say otherwise, which is exactly what JSON Web Tokens do. Decoding here accepts all four combinations without being told which one you have.

Base64 is not encryption#

RFC 4648's own security section says it plainly: "Base encoding visually hides otherwise easily recognized information, such as passwords, but does not provide any computational confidentiality." It goes on to point out that encoding a secret actually gives an attackermore to work with, since it "does increase the amount of plaintext available".

The related mistake is thinking a JWT is opaque. The header and payload of a token are base64url, not encrypted, and anyone holding the token can read both. Only the signature stops them changing it.

jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.dBjftJeZ4CVP

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9   header      {"alg":"HS256","typ":"JWT"}
eyJzdWIiOiIxMjM0In0                    payload     {"sub":"1234"}
dBjftJeZ4CVP                           signature   raw bytes, not text

Where you actually meet Base64#

  • data: URIs. RFC 2397 defines the formdata:[<mediatype>][;base64],<data>, which is how a small icon or font gets inlined into CSS or HTML. The data: URI option above builds one using the media type your browser reports for the file you picked.
  • Email attachments. MIME carries binary files this way. RFC 2045 requires the encoded stream to be "represented in lines of no more than 76 characters each", which is what the wrap option produces.
  • JWT segments. RFC 7515 defines its encoding as base64url "with all trailing '=' characters omitted... and without the inclusion of any line breaks, whitespace, or other additional characters". URL-safe on, padding off, wrapping off.
  • HTTP Basic authentication. RFC 7617 joins the user name and the password with a colon and encodes the result with the standard alphabet. Aladdin:open sesamebecomes QWxhZGRpbjpvcGVuIHNlc2FtZQ==, which is the RFC's own example, and which you can paste above to see how little protection it offers.
  • Keys and certificates. PEM files are Base64 between-----BEGIN----- lines, wrapped at a fixed width.

Why btoa() breaks on Unicode#

JavaScript's built-in btoa() does not take bytes. It takes a string and treats each code unit as one byte, so it only works when every code unit is below 256. Give ité and it throws an InvalidCharacterError: "The string contained a character that did not fit in a single byte."

The fix is to decide on an encoding rather than letting btoa assume one. Turn the string into bytes with TextEncoder, which produces UTF-8, and encode those bytes. That is what this page does, so héllo encodes to aMOpbGxv here and in Python, Go and every other language that uses UTF-8.

Current browsers have a direct route: Uint8Array.prototype.toBase64() andUint8Array.fromBase64(), shipped in Chrome 140, Firefox 133, Safari 18.2 and Node 25. This page uses them when they exist and falls back to its own encoder when they do not, so the result is identical either way.

Base64 in code#

JavaScript

javascript
// Wrong: btoa() works on code units, not characters.
btoa('héllo');
// InvalidCharacterError: the string contained a character
// that did not fit in a single byte.

// Right, and available in current browsers and Node 25+:
const bytes = new TextEncoder().encode('héllo');
bytes.toBase64();                      // 'aMOpbGxv'
bytes.toBase64({ alphabet: 'base64url', omitPadding: true });

new TextDecoder().decode(Uint8Array.fromBase64('aMOpbGxv'));  // 'héllo'

// Right, and works everywhere, including older browsers:
function bytesToBase64(bytes) {
  const binary = Array.from(bytes, (b) => String.fromCodePoint(b)).join('');
  return btoa(binary);
}
bytesToBase64(new TextEncoder().encode('héllo'));  // 'aMOpbGxv'

Python

python
import base64

base64.b64encode(b'foobar')            # b'Zm9vYmFy'
base64.b64encode('héllo'.encode())     # b'aMOpbGxv'
base64.b64decode('Zm9vYmFy')           # b'foobar'
base64.b64decode('Zm9vYmFy').decode()  # 'foobar'

base64.urlsafe_b64encode(b'\xfb\xff\xbf')  # b'-_-_'

# By default, characters outside the alphabet are discarded rather
# than rejected. Pass validate=True to get a binascii.Error instead.
base64.b64decode('Zm9v YmFy', validate=True)

Go

go
import "encoding/base64"

base64.StdEncoding.EncodeToString([]byte("foobar"))  // "Zm9vYmFy"
base64.URLEncoding.EncodeToString([]byte{0xfb, 0xff, 0xbf})  // "-_-_"

// The "Raw" encodings are the same alphabets without the "=" padding,
// which is what JWTs and most URL payloads use.
base64.RawURLEncoding.EncodeToString([]byte("f"))    // "Zg"

out, err := base64.StdEncoding.DecodeString("Zm9vYmFy")  // []byte("foobar")

Shell

shell
# Encode. GNU base64 wraps at 76 characters by default; -w 0 turns that off.
printf 'foobar' | base64            # Zm9vYmFy
printf 'foobar' | base64 -w 0

# Decode.
printf 'Zm9vYmFy' | base64 -d       # foobar

# A file, and back again.
base64 -w 0 logo.png > logo.b64
base64 -d logo.b64 > logo.png

# The flags differ outside GNU coreutils: on macOS the decode flag is -D
# and line breaking is -b. Check "man base64" if a flag is rejected.

Nothing here leaves your browser#

Text and files are read and converted on your machine, by your browser, after the page has loaded. Nothing is uploaded, and there is nowhere to upload it to: this site is static files with no backend. The file picker and the drop zone use the browser's own file reading, so the bytes never travel further than the tab they are in. Disconnect from the network and the tool still works.

Files are capped at 10 MB, because the whole file and its encoded form both have to fit in the page's memory at once.

Questions people ask#

What do the = signs at the end mean?

They are padding, and they tell you how much of the last group is real data. One= means the last group carried 2 bytes, two = means it carried 1. They add no information beyond the length, which is why formats that already know the length, like JWT, drop them. Decoding here works with or without them.

Can Base64 contain spaces or newlines?

Not as data, but they are common in the wild because MIME wraps long output into lines. RFC 4648 says decoders MUST reject characters outside the alphabet unless the format they are implementing says otherwise, and MIME is exactly that exception. This tool follows MIME and skips whitespace, while still rejecting any other stray character and telling you where it is.

Why does my decoded text look like garbage?

Because it is not text. Base64 encodes bytes, and those bytes may be a PNG, a protobuf or a gzip stream. When the decoded bytes are not valid UTF-8, this page tells you so and offers them as a download instead of printing replacement characters at you. If you expected text, check that the data was UTF-8 to begin with and not, say, UTF-16.

Is URL-safe Base64 the same length?

Yes. It is the same encoding with two characters renamed, so the length is identical. Dropping the padding makes it shorter by one or two characters, never more.

Can I decode a Base64 image here?

Yes. Paste the Base64, with or without the data:image/png;base64, header, and use the download button to save the bytes. The file is rebuilt in your browser; rename it to match the real type if you need the right extension.

How do I check my encoder is correct?

Use the test vectors in RFC 4648 section 10. They are short and they exercise every padding case:f to Zg==, fo to Zm8=, foo toZm9v, foob to Zm9vYg==, fooba toZm9vYmE=, foobar to Zm9vYmFy. This tool is tested against all of them, plus a round trip of all 256 byte values.

  • Percent-encode and decode URLs and query strings, with encodeURIComponent, encodeURI, strict RFC 3986 and form encoding, plus a breakdown of the query parameters.

  • Generate random v4 and time-ordered v7 UUIDs in your browser, one at a time or up to 1000 at once, and inspect any UUID you paste in.