Base64 is a way of writing arbitrary bytes using only 64 printable characters: A–Z, a–z, 0–9, + and /. Base64 encoding takes every 3 bytes of input and turns them into 4 characters, so the result is about 33% larger than the original. It exists so that binary data such as images, keys or compressed files can travel through channels built for text, like email, JSON, HTML or URLs. It is not encryption: there is no key, and anyone can decode it.
You can follow every example below in the Base64 encoder and decoder. For values headed into a query string, the URL encoder and decoder handles the percent-escaping; once a token is decoded, the JSON formatter makes its payload readable; the image compressor trims files before you inline them; and the character counter tells you exactly how long the encoded string came out.
Why Base64 encoding exists
Early email was specified for 7-bit ASCII text. Mail servers along the route were free to strip the high bit, rewrap long lines or choke on control bytes, which made sending a raw JPEG a gamble. MIME (RFC 2045, published in 1996) solved this by mapping binary content onto a small alphabet that every relay would pass through untouched. The general-purpose definition developers cite today lives in RFC 4648.
The original problem never really went away; it just moved. JSON has strings but no byte type. HTML and CSS are text files. HTTP headers are text. Whenever bytes have to ride inside one of these, Base64 is the default bridge, which is why you meet it in API payloads, in PEM certificates that start with -----BEGIN CERTIFICATE-----, in raw email source and all over browser developer tools.
The alphabet, in order
Each character stands for a 6-bit number between 0 and 63. Uppercase letters cover 0–25, lowercase letters 26–51, digits 52–61, then + is 62 and / is 63. The equals sign is not part of the alphabet at all; it is padding and carries no data. Because the mapping is fixed and public, any two correct implementations produce identical output for identical bytes.
How the 3-to-4 conversion works
Encoding the string Hi! by hand shows the whole mechanism:
- Take the bytes: H = 72, i = 105, ! = 33.
- Write them in binary:
01001000 01101001 00100001.
- Regroup the 24 bits into four 6-bit chunks:
010010 000110 100100 100001.
- Convert each chunk to decimal: 18, 6, 36, 33.
- Look them up in the alphabet: S, G, k, h. The output is
SGkh.
Twenty-four is the smallest number divisible by both 8 and 6, which is why the algorithm works in 3-byte blocks. Decoding runs the same steps backwards: every character becomes 6 bits, and every four characters become three bytes again.
Where the equals signs come from
Input rarely divides neatly by three. When one or two bytes are left over, the encoder pads the final chunk with zero bits and appends = so the output length stays a multiple of four:
| Input | Bytes | Encoded | Padding characters |
| Hi! | 3 | SGkh | 0 |
| Hi | 2 | SGk= | 1 |
| H | 1 | SA== | 2 |
You will never see three padding characters, and a string whose length leaves a remainder of 1 when divided by four cannot be valid. That second fact is a handy sanity check when a value has been truncated by a sloppy copy-paste or a database column that was too short.
Accented text and emoji
The algorithm encodes bytes, not letters, so the character encoding of the text decides the output. In UTF-8 the word café is five bytes, because é takes two, and encodes to Y2Fmw6k=. The euro sign alone is three bytes and becomes 4oKs. If the sender encodes UTF-8 text and the receiver reads the decoded bytes as Windows-1252, é comes back as é. That kind of garbage is almost never the encoder's fault; it is two systems disagreeing about text encoding.
The 33% size overhead, and the alternatives
Output length is 4 × ceil(n / 3) characters for n input bytes. A 60 KB image becomes roughly 80 KB of text; a 6 MB attachment becomes about 8 MB. MIME also inserts a line break every 76 characters, which pushes the overhead in email closer to 37%. Base64 sits in the middle of a family of binary-to-text schemes:
| Scheme | Ratio | Overhead | Typical home |
| Hex (Base16) | 2 chars per byte | 100% | Hashes and checksums |
| Base32 | 8 chars per 5 bytes | 60% | Authenticator app secrets |
| Base64 | 4 chars per 3 bytes | About 33% | Email, JSON, JWT, data URIs |
| Ascii85 | 5 chars per 4 bytes | 25% | PDF and PostScript streams |
The scheme wins on simplicity and universal library support rather than on density. HTTP compression claws some of the overhead back on text-heavy payloads, but encoding an already compressed JPEG or ZIP leaves most of the penalty in place. If a file is large, send it as a file.
Base64url and the trouble with URLs
Two characters of the standard alphabet misbehave in web addresses. A slash is a path separator, and a plus sign in a form-encoded query string is read as a space. Section 5 of RFC 4648 defines base64url to avoid both: - replaces +, _ replaces /, and most formats drop the padding because the length can be inferred.
The string <<???>> makes the difference visible. The standard alphabet gives PDw/Pz8+Pg==; base64url gives PDw_Pz8-Pg. Put the standard form in a query string without escaping it and a server may receive a space where the plus used to be. Either switch to base64url or percent-encode the value first, which turns it into PDw%2FPz8%2BPg%3D%3D.
JSON Web Tokens are the best-known users of the URL-safe variant. The first two dot-separated parts of a JWT are unpadded base64url-encoded JSON, which is why so many tokens begin with eyJ: that is what the opening {" of a JSON object looks like after encoding when a letter follows it. Decode the header segment eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 and you get {"alg":"HS256","typ":"JWT"}.
Data URIs: inlining files with Base64 encoding
A data URI, defined in RFC 2397, embeds a file directly inside HTML or CSS using the form data:[media type][;base64],data. Every PNG starts with the same 8-byte signature, so every encoded PNG starts with iVBORw0KGgo. A typical use in a stylesheet looks like this:
.icon { background-image: url("data:image/png;base64,iVBORw0KGgo..."); }
Inlining saves a request, and for a tiny icon or a self-contained email template that can be worth it. For anything larger the trade turns bad:
- The file grows by a third and is re-downloaded with every copy of the HTML or CSS that contains it, because the browser cannot cache it separately.
- An image inlined in a stylesheet delays rendering until the whole stylesheet has arrived.
- Used on ten pages, the same image travels ten times.
A sensible rule is to inline only assets of a few kilobytes and to compress them before encoding. For SVG icons, plain URL-encoded markup (data:image/svg+xml,...) is often shorter than the encoded version, since SVG is already text.
Base64 is not encryption
This misunderstanding costs real money. The encoding changes the shape of data, not its secrecy. There is no key and no secret parameter, and every mainstream language reverses it in a single call.
HTTP Basic authentication shows the consequence. The browser joins username and password with a colon, encodes the result and sends it in a header, as described in RFC 7617. The pair user:pass travels as Authorization: Basic dXNlcjpwYXNz, and anyone who can see that header can read the password. What protects it is TLS, not the encoding.
- Need confidentiality? Use real encryption, such as AES with a properly managed key.
- Storing passwords? Use a deliberately slow, one-way password hash like bcrypt or Argon2. An encoded password is a plaintext password with extra steps.
- Need tamper detection? Use a signature or an HMAC. That is the job of the third segment of a JWT; the header and payload are readable by anyone holding the token.
Common bugs and how to spot them
- btoa() and non-Latin text. In browsers,
btoa("€") throws an InvalidCharacterError because the function only accepts characters in the Latin-1 range. Convert the string to UTF-8 bytes with TextEncoder first. The tool on this site does that for you.
- The newline from echo.
echo "hello" | base64 prints aGVsbG8K, not aGVsbG8=, because echo appends a line feed that gets encoded too. Use printf '%s' hello | base64 or echo -n. This one regularly produces "invalid API key" errors that look impossible.
- Wrapped lines. MIME wraps at 76 characters, PEM at 64, and GNU
base64 wraps at 76 by default unless you pass -w 0. Strict decoders reject the embedded newlines.
- Missing padding. Libraries that expect padding fail on unpadded base64url. Append
= until the length is a multiple of four.
- Plus turned into space. A standard value placed in a query string without escaping arrives with spaces where the plus signs were, and the decoder either fails or returns corrupted bytes.
- Decoding binary as text. Decoding an encoded image and displaying it as a string produces noise. The tool tells you when the decoded bytes are not valid UTF-8 text.
Encoding and decoding in the tool
- Paste your text or your encoded string into the input box.
- Pick a mode: Auto, Encode or Decode. Auto only decodes input that genuinely looks encoded and turns into readable text; everything else gets encoded.
- Force the direction for short, ambiguous values.
QUFB could pass for an abbreviation, yet it is also the valid encoding of AAA, so Auto will decode it.
- Copy the result. When decoding, base64url (with
- and _, padded or not) and MIME-style wrapped lines are accepted.
Everything happens in your browser. The input is not sent to the server; the page only increments an anonymous usage counter. Even so, keep production secrets out of any web form you do not control, as a matter of habit.
Frequently asked questions
Why does an encoded string end with = or ==?
That is padding. If the input length is not a multiple of three bytes, the output is filled out to a multiple of four characters. One equals sign means the last block held two bytes; two mean it held one. Formats such as JWT drop the padding entirely.
Does Base64 compress data?
No, it does the opposite. The encoded form is about four thirds of the original size. To make something smaller, compress it first with gzip, a ZIP archive or a better image format, and encode afterwards only if the channel requires text.
Is it safe to put an encoded value in a URL?
Only in its base64url form or after percent-encoding. The standard alphabet contains +, / and =, all of which have special meaning in URLs or form data.
How can I tell whether a string is Base64?
Look for a length that is a multiple of four, characters limited to the alphabet, and at most two trailing equals signs. Beyond that, the only real test is decoding it and checking whether the bytes make sense; plenty of ordinary words pass the format check by accident.
If you are trying to make files smaller rather than just portable, read what minification does and how it differs from gzip. The guide what is a UUID shows how base64url squeezes a 16-byte identifier into 22 characters. For the JSON that so often wraps encoded fields, see working with JSON data, and before inlining vector icons, what an SVG file is explains when plain text beats encoding.