Base64 converts arbitrary bytes into a short alphabet of printable characters so that binary data can travel through channels designed for text. It is not encryption, it is not compression, and it does not protect anything. It is a transport format, and almost every problem people hit with it comes from forgetting that distinction.

Encoding and decoding are mechanical operations that always produce a deterministic result. When decoded output looks like garbage, the encoding step is rarely at fault: the input was truncated, the variant was wrong, or the resulting bytes were interpreted with the wrong character set. This guide walks through the mechanics and then through the failures.

What Base64 actually does

Base64 reads the input three bytes at a time. Three bytes are twenty-four bits, which split evenly into four groups of six bits. Each six-bit group has sixty-four possible values, and each value maps to one character in the Base64 alphabet. That is the whole algorithm: regroup bits, look up characters.

The consequence is a predictable size increase. Every three bytes of input become four characters of output, so encoded data is roughly thirty-three percent larger than the original. A one megabyte file becomes about 1.37 megabytes of Base64 text. That overhead is the price of passing binary through a text-only channel.

The Base64 character set and padding

Standard Base64, defined in RFC 4648, uses the uppercase letters A to Z, the lowercase letters a to z, the digits 0 to 9, and the two symbols + and /. That is sixty-four characters, hence the name.

When the input length is not a multiple of three, the final group is incomplete and the encoder appends = characters so the output length stays a multiple of four. One leftover byte produces two padding characters, two leftover bytes produce one. Padding carries no data; it only signals how many bits of the last group are real.

Encoding text step by step

Take the string Hi. In UTF-8 those are the bytes 0x48 and 0x69, which is sixteen bits. Sixteen bits split into two full six-bit groups and one group of four bits, which is zero-padded to six. The three resulting values map to S, G, and k, and one = is appended, giving SGk=.

Notice that the text had to become bytes before anything else happened. Base64 encodes bytes, not characters, so an encoder must first decide how to serialize the string. Practically every modern system uses UTF-8 for this, but the choice is real, and mismatched assumptions on the two ends are the single most common source of corrupted output.

How to decode Base64 string data back to text

Decoding reverses the process. The decoder maps each character back to its six-bit value, concatenates the bits, and slices the stream into eight-bit bytes, discarding whatever padding bits remain. The result is a byte sequence, and only then does a character set turn those bytes back into readable text.

This two-stage nature matters when you paste a string into a Base64 decoder and see something unreadable. The decoder may have produced exactly the right bytes. If those bytes are a PNG, a protocol buffer, or a gzip stream, rendering them as text will always look like noise, and the correct next step is to save them as a file rather than to keep decoding.

Why decoded text shows the wrong characters

Mangled accents and question-mark boxes almost always mean a character set mismatch. If a producer encoded text as Windows-1252 or ISO-8859-1 and the consumer decodes the bytes as UTF-8, every non-ASCII character breaks. The Base64 layer performed correctly; the disagreement is one level below it.

Fix this by making the character set explicit rather than inferred. Agree that payloads are UTF-8, state it in the API contract, and set it on HTTP responses through the Content-Type header. When you must consume a legacy source, decode the Base64 to raw bytes first and then apply the specific legacy character set deliberately.

Common decoding errors and what they mean

An "invalid character" error usually means the string picked up whitespace, quotation marks, or line breaks during a copy, or that it is actually the URL-safe variant containing - and _. An "invalid length" error means the string was truncated, or that padding was stripped by a system that considered it optional.

Be careful with strict decoders that reject any character outside the alphabet, because many real-world producers wrap output at seventy-six characters per line. Some decoders ignore those newlines and some do not. When a payload fails in one tool and succeeds in another, compare their tolerance for whitespace before assuming the data is broken.

What Base64 does not do

Base64 is fully reversible by anyone, with no key and no secret. Encoding a password, an API token, or a customer record hides nothing; it merely changes the presentation. Treat Base64 strings in logs, configuration files, and query parameters as if they were written in plain text, because for any attacker they are.

It is also the wrong choice when a channel can carry binary directly. Embedding large images as data URIs or Base64 fields inflates payloads by a third, defeats caching, and increases parse cost. Use it where a text-only boundary genuinely exists, such as email attachments, JSON fields, and HTTP basic authentication headers, and use raw bytes everywhere else.

The Base64 encoding algorithm and character set

The Base64 encoding algorithm is small enough to state completely. Read three bytes, split the twenty-four bits into four groups of six, map each group through the alphabet, and pad the final group. The Base64 decode algorithm is the same steps in reverse.

The Base64 encoding character set is fixed: index 0 to 25 are A to Z, 26 to 51 are a to z, 52 to 61 are 0 to 9, and the last two are + and /, with = reserved for padding. A Base64 encoded string therefore contains only those characters, which is exactly why an unexpected - or _ means you are looking at the URL-safe variant.

Base64 encode and decode a string in code

Every runtime exposes both directions. The table shows how to Base64 encode a string and how to decode Base64 to string form, including the byte-level calls you need for binary data.

EnvironmentEncodeDecode
Base64 to text Pythonbase64.b64encode(b)base64.b64decode(s)
Base64 encode bytes Pythonb64encode(text.encode("utf-8"))b64decode(s).decode("utf-8")
Base64 to text js, in the browserbtoa(s)atob(s)
Base64 decode browser, binary safeUint8Array.fromBase64(s)Fallback: map atob output to bytes
Node, Base64 encode byte arraybuf.toString("base64")Buffer.from(s, "base64")
Base64 to text c#Convert.ToBase64String(b)Convert.FromBase64String(s)
JavaBase64.getEncoder().encodeToString(b)Base64.getDecoder().decode(s)
Decode base64 Angularbtoa() via a serviceatob(); guard against binary
Base64 decode AndroidBase64.encodeToString(b, NO_WRAP)Base64.decode(s, NO_WRAP)

A short Base64 text example makes the shape obvious: the six characters of Base64 a text input such as Hello encode to SGVsbG8=, and running Base64 decode a string operation on that returns the original. A Base64 to text online panel and a Base64 to text converter online do exactly this in the browser, while a Base64 to text file workflow writes the decoded bytes out instead of displaying them.

Two portability notes. The browser pair btoa and atob operate on single-byte character codes, so passing text with accents throws; encode to UTF-8 bytes first. And Android's default flag wraps lines, which is why NO_WRAP appears above: without it a token acquires newlines that a server will reject.

Base64 decode to file, image, PDF, and hex

A Base64 decoder to file workflow differs from text decoding only in what you do with the bytes. Write them out rather than rendering them, then let the file signature tell you what you have. A Base64 decoder to image, a Base64 decoder to PDF, and a Base64 decoder to hex view are the same decode followed by three different ways of displaying the result.

The media type decides how you handle the result. Decode Base64 image data and write a .png or .jpg; decode Base64 to PDF and write a .pdf, which you can confirm from the %PDF- signature at the start; decode Base64 audio and the container extension follows the same rule. Whether you decode Base64 a PDF from an email attachment or decode Base64 data from an API field, the decode step is identical and only the file extension changes.

Working at the byte level makes this explicit. A Base64 decode bytes call returns a byte array rather than a string, which is what you want for anything non-textual; a Base64 decode byte array result written straight to disk needs no character set at all. In the other direction, Base64 encode binary input the same way: read the file as bytes, never as text, because a Base64 encode binary file step that goes through a string will corrupt anything that is not valid UTF-8.

Compressed payloads add one step. A Base64 decode and unzip or Base64 decode and decompress sequence must decode first and inflate second, because the compression happened before the encoding. If a Base64 decode and download of the result produces a file your viewer rejects, check for a gzip signature before assuming the payload is corrupt.

Base64 for certificates and basic authentication

A Base64 encoded certificate is what the PEM format is: DER bytes rendered as Base64 between header and footer lines. To decode Base64 certificate data, strip those lines and the newlines, then decode the remainder to binary DER. The reverse produces a value safe to paste into a configuration file.

Base64 encode basic auth follows a strict recipe: join the username and password with a single colon, encode the UTF-8 bytes, and prefix the result with Basic . The most common failure is a trailing newline captured from a shell, which produces a header that looks correct and never authenticates.

A dependable workflow

Start by identifying what the decoded bytes are meant to be, because that determines whether you want text or a file. Check the string for the URL-safe alphabet and for stray whitespace before decoding. Decode with a strict tool, then interpret the bytes with an explicitly chosen character set.

Finally, think about where the string has been. A Base64 payload that contains credentials should never be pasted into a service that uploads input, so prefer a decoder that runs entirely in the browser. Encoding is not protection, and the safest habit is to treat every encoded value as readable by default.