Skip to main content
ToolMaple

Base64 Encode and Decode

Turn text into Base64 and back; UTF-8 characters survive the round trip.

Last updated:

Result
SGVsbG8sIHdvcmxkISBDYWbDqSDwn42B

What Base64 does, and why a bare btoa() breaks

Base64 writes arbitrary bytes using only 64 characters that no text channel is likely to mangle: A-Z, a-z, 0-9, + and /. The encoder reads 3 bytes at a time, splits those 24 bits into four groups of 6, and writes each group as one character. Three bytes in, four characters out, which is why the result is about a third longer than the input. When the length is not a multiple of 3, one or two = characters pad the tail so the output divides evenly by 4. RFC 4648 is the specification.

The format exists because many systems were only ever designed to carry text, and mail servers, HTTP headers and JSON string fields all have opinions about control characters, line endings and bytes above 127. That is why Base64 keeps turning up: email attachments, the Authorization: Basic header, data URLs, the three parts of a JWT, PEM certificate blocks, and binary blobs pushed through an API that only speaks JSON.

The naive browser approach fails on the first non-English word you try. btoa() treats each character of a JavaScript string as a single byte, so it throws InvalidCharacterError on anything above U+00FF: Greek, Cyrillic, Japanese, Korean, Chinese and every emoji. Going the other way, atob() hands back raw byte values, and displaying those directly gives the classic mojibake where café comes back as café. This page runs the text through TextEncoder on the way in and TextDecoder on the way out, so the round trip is lossless UTF-8 for anything you can type or paste.

Which Base64 variant the job needs

  • Standard, padded (RFC 4648 section 4): the + and / alphabet with trailing =. This is what the tool above produces and what Basic Auth headers, data URLs and most API fields expect.
  • URL and filename safe, known as base64url (RFC 4648 section 5): identical except that + becomes - and / becomes _. Use it in query strings, path segments and filenames, where a slash means something else and a plus can be read as a space.
  • base64url with no padding: the same alphabet with the = characters dropped. This is what JWTs use for the header, payload and signature, so a JWT part almost never ends in an equals sign.
  • MIME Base64 (RFC 2045): standard alphabet broken into lines of at most 76 characters, used in email bodies. PEM blocks use the same idea with 64-character lines. Decoders are expected to ignore the line breaks.
  • Base32 and hex: the neighbors in the same RFC. Base32 uses a case-insensitive alphabet and is how authenticator app shared secrets are written. Hex doubles the size but is readable at a glance, which is why hashes are printed that way.
  • Not Base64 at all: if you are escaping text for a URL rather than carrying bytes through one, you want percent-encoding from the URL encoder instead.

How to build an HTTP Basic Auth header

  1. Leave the mode on Text → Base64.
  2. Type the username, a single colon, then the password, with no spaces around the colon. The first colon separates them, so a password may contain colons but a username may not.
  3. Press Copy and build the header as Authorization: Basic followed by a space and the encoded value.
  4. Check the result by decoding it back. If you generated the value at a shell prompt instead, a trailing newline is the usual culprit behind a header that looks right and still returns 401.

This header is your credentials in plain sight, reversible by anyone who captures the request, so Basic Auth belongs only on HTTPS. Most HTTP clients will assemble it for you, for example curl -u user:password.

How to decode a string you found in a log or a URL

  1. Switch the mode to Base64 → Text and paste the string. Surrounding quotes and trailing commas are the most common reasons a paste fails, so trim them first.
  2. If the string contains - or _, it is base64url. Replace - with + and _ with / before decoding.
  3. If it has dots in it, you are holding a JWT rather than a single Base64 blob. Split on the dots and decode the first two parts separately, or use the JWT decoder, which does the split and the base64url swap for you.
  4. If the output is unreadable symbols rather than an error, the bytes were never text: a compressed payload, an image or an encrypted value all decode cleanly and still look like noise.
  5. If the result is JSON, paste it into the JSON formatter to see the structure.

Getting the result where it needs to go

The output is one unbroken line, which is what headers, JSON fields and environment variables want. It is shown with soft wrapping, so what looks like several lines is still a single string when you copy it.

  • Into JSON: paste it straight into a string value. None of the Base64 characters need escaping, so no backslashes appear.
  • Into a URL: standard Base64 is not URL safe. Either percent-encode it, where + becomes %2B, / becomes %2F and = becomes %3D, or switch to the base64url alphabet before you paste.
  • Into YAML or a config file: keep it on one line, and turn off any editor setting that hard-wraps long lines, because an inserted newline changes the value.
  • iPhone, iPad and Android: press and hold in the target field and tap Paste. If the string is long, check its end, because some fields truncate without saying so.
  • Windows and Mac: Ctrl+V and Cmd+V. Pasting into a terminal is where trouble starts, since a shell may treat / and + as part of a pattern, so wrap the value in single quotes.

The same bytes in different languages and runtimes

Every platform has Base64 built in; the differences are in the defaults, namely which alphabet, whether padding is written, whether long output is wrapped, and how strict the decoder is. Browsers give you btoa() and atob(), which work on bytes rather than text, so the UTF-8 conversion is your job:

const b64 = btoa(String.fromCharCode(...new TextEncoder().encode(text)))
const text = new TextDecoder().decode(
  Uint8Array.from(atob(b64), c => c.charCodeAt(0)),
)

Node.js hides the byte handling inside Buffer, and also ships a base64url encoding name that applies the URL safe alphabet and drops padding:

Buffer.from(text, 'utf8').toString('base64')
Buffer.from(b64, 'base64').toString('utf8')
Buffer.from(text, 'utf8').toString('base64url')

Python 3 works on bytes at both ends, so b64encode returns bytes rather than a string. It has a separate URL safe pair, and its decoder ignores unknown characters unless you pass validate=True:

import base64
base64.b64encode(text.encode()).decode()
base64.b64decode(b64, validate=True).decode()
base64.urlsafe_b64encode(text.encode()).decode()

Go makes the choice explicit with four encodings in encoding/base64: StdEncoding and URLEncoding write padding, RawStdEncoding and RawURLEncoding do not. Java splits the same way through Base64.getEncoder(), getUrlEncoder() and getMimeEncoder().

The command line is where the classic mistake lives. echo adds a trailing newline that becomes part of the encoded bytes and produces a credential the server rejects, so reach for printf. GNU coreutils also wraps output at 76 columns unless you turn that off:

printf '%s' 'user:password' | base64 -w 0   # GNU, no line wrapping
printf '%s' 'user:password' | base64        # macOS, no wrapping by default
printf '%s' 'dXNlcjpwYXNz' | base64 -d      # decode

On macOS the tool is the BSD one, which does not take -w; it has long accepted -D for decoding and takes -d on current versions, so run base64 --help before scripting either flag. Databases expose Base64 too, through encode and decode in PostgreSQL and TO_BASE64() and FROM_BASE64() in MySQL; both wrap long output across several lines by default, so a value selected out of a query may need its newlines stripped first.

Limits, privacy and honest caveats

What leaves your browser: nothing. The conversion runs locally with TextEncoder, TextDecoder and the built-in btoa() and atob(). Nothing you type is uploaded, stored or logged, and the result block is masked against session recording. Even so, prefer a local command for production secrets: a live credential pasted into any web page has been somewhere it did not need to be.

Base64 is not security, and not compression. There is no key, so it offers no confidentiality, and no checksum, so an altered string usually still decodes to something. Reading a JWT payload tells you what the token claims, not whether the claim is true; only checking the signature does that. Encoding also adds about a third to the size, and an image inlined as a data URL cannot be cached on its own and may be refused outright by a strict Content Security Policy.

What this decoder accepts. The browser applies the forgiving-base64 algorithm from the WHATWG Infra standard, which strips ASCII whitespace first, so a MIME-wrapped block keeps its line breaks and still decodes, as does a string whose padding was removed. It rejects the base64url alphabet, since - and _ are not standard Base64 characters, and any length that leaves a remainder of 1 when divided by 4.

Size and accessibility. The whole string is converted in one pass, so a few megabytes of text will make the tab stutter; for anything file-sized, use the command line above. The output is ordinary selectable text in a monospace block rather than an image, so a screen reader can read it and you can select it by hand instead of using the copy button.

Frequently asked questions

Is Base64 encryption?

No. Base64 is an encoding, not a cipher. There is no key, and anyone who sees the string can turn it straight back into the original bytes, which is exactly what the decode mode on this page does. Putting a password, an API key or a private key into Base64 hides it from a casual glance and from nothing else. If you need secrecy, encrypt the data with something like AES first and Base64 the ciphertext afterwards if you need it to survive a text-only channel.

Why does the encoded string come out longer than what I typed?

Base64 turns every 3 bytes of input into 4 output characters, so the result is about 33 percent longer, plus one or two padding characters at the end and any line breaks the encoder adds. That growth is the price of the format: it buys you a string made only of letters, digits, plus and slash, which survives mail servers, HTTP headers and JSON string fields without being mangled. Base64 never compresses. If size matters, compress with gzip or brotli first, then encode.

What do the equals signs at the end mean?

They are padding. Base64 works on groups of 3 bytes, so when the input length is not a multiple of 3 the last group is short and the encoder pads the output to a multiple of 4 characters. One leftover byte produces two equals signs, two leftover bytes produce one, and an exact multiple of 3 produces none. Padding carries no data, which is why the base64url variant used in JWTs is allowed to drop it.

Why does btoa() in the browser throw on accented text or emoji?

btoa() accepts only characters in the range U+0000 to U+00FF, because it treats each character of the string as one byte. Anything above that, including most accented forms from some sources, CJK text and every emoji, raises an InvalidCharacterError. The fix is to convert the text to UTF-8 bytes first with TextEncoder and hand those bytes to btoa(), which is what this page does, so the round trip is lossless for any Unicode text.

My string will not decode. What is usually wrong?

Four causes cover almost every case. The string contains characters that are not in the Base64 alphabet, often a stray space, a quote or a line break copied along with it. The length is not a multiple of 4 and the padding was stripped. The string is base64url, using hyphen and underscore, and a strict standard decoder rejects them. Or the text was truncated when it was copied out of a log or a terminal. Try trimming whitespace, then swapping hyphen for plus and underscore for slash.

What is the difference between Base64 and base64url?

They encode the same bytes with two different characters at positions 62 and 63. Standard Base64, defined in RFC 4648 section 4, uses plus and slash. The URL and filename safe variant in section 5 uses hyphen and underscore instead, because plus and slash need escaping inside a URL and slash is a path separator. JWTs use the URL safe alphabet with the padding removed. This page emits the standard alphabet, so swap the two characters yourself when a system asks for base64url.

Can I encode an image or a PDF with this page?

This page takes text, not files, so paste a string rather than dropping a file on it. Encoding a binary file means reading its raw bytes and encoding those, which is what a data URL for an inline image contains. Be aware that data URLs are roughly a third larger than the file, they cannot be cached separately by the browser, and a strict Content Security Policy may block them, so they suit small icons rather than photographs.

Does anything I paste here get uploaded?

No. Encoding and decoding run in your browser with TextEncoder, TextDecoder and the built-in btoa and atob functions. The text never leaves the page, is not sent to a server and is not written to storage, and the on-page result is masked so that session recording cannot capture it. Reloading the tab clears everything, so copy anything you want to keep before you navigate away.

Is it safe to decode a JWT or a token I found in a log?

Decoding is safe for you in the sense that it is local arithmetic on this page, but treat what you find with care. The header and payload of a JWT are only Base64 encoded, so decoding them proves nothing about whether the token is genuine; only checking the signature does that. A token pasted out of a log may still be valid and usable by anyone who has it, so avoid pasting live credentials into any tool you did not read the source of, and rotate one if you suspect it leaked.

Sources

  • IETF, RFC 4648, The Base16, Base32, and Base64 Data Encodings (the standard alphabet in section 4, the URL and filename safe alphabet in section 5, and the padding rules).
  • IETF, RFC 2045, MIME Part One (Base64 for email bodies, including the 76-character line limit).
  • IETF, RFC 7519, JSON Web Token (why JWT parts use base64url with the padding removed).
  • MDN, Base64 (the browser API, the Unicode problem with btoa(), and the TextEncoder workaround this page uses).
  • WHATWG, Infra Standard, forgiving-base64 decode (exactly which inputs atob() accepts and rejects).
  • The Node.js Buffer documentation, the Python base64 module documentation, the Go encoding/base64 package documentation and the PostgreSQL and MySQL string function references cover the per-platform behavior described above; each vendor can change its defaults, so confirm against the version you are running.

More developer tools

If the string you are decoding has dots in it, the JWT decoder splits it and reads the claims. If what comes out is JSON, the JSON formatter will indent it and point at the line where it breaks. For escaping text inside a URL rather than carrying bytes through one, use the URL encoder and decoder, and when you need an identifier rather than an encoding, the UUID generator produces them in the browser as well.

Related tools

Privacy: encoding and decoding happen entirely in your browser with TextEncoder, TextDecoder, btoa() and atob(). Your text is never sent to a server, never stored and never logged, and the on-page result is masked from session recording. Analytics records only that the page was viewed.