Base64 Encoding Explained: How It Works and When to Use It
You open an API response and one field is a wall of letters and numbers ending in two equals signs. Or an email header contains a paragraph that is clearly not English. Or a config file has a certificate that looks like someone leaned on the keyboard.
That is base64, and it is doing something specific: carrying data that is not text through a channel that only handles text.
It shows up constantly once you know what it looks like, and it is regularly misunderstood in one important way. Here is how it works, why the padding characters appear, and the single mistake worth never making with it.
The problem base64 solves
Computers store everything as bytes, and a byte can hold any of 256 values. Text protocols were not built for that.
Email was designed to carry seven-bit ASCII. Many older systems treat certain byte values as control signals rather than data: a byte that happens to be 0x00 can terminate a string, 0x0D and 0x0A can be rewritten as line endings, and values above 127 may be stripped or mangled entirely.
Send a JPEG through a channel like that and it arrives corrupted. Not because anything failed, but because the channel did exactly what it was designed to do to bytes it considered special.
Base64 sidesteps this by restricting the output to 64 characters that every system agrees are safe: A to Z, a to z, 0 to 9, plus two symbols. Nothing in that set can be mistaken for a control character or altered in transit.
The cost is size. Base64 output is roughly 33% larger than the input, which is the price of using six bits of information per eight-bit character.
How the conversion actually works
Base64 reads your data three bytes at a time. Three bytes is 24 bits, and 24 divides evenly into four groups of six. Each six-bit group becomes one output character, so every three input bytes produce four output characters.
Six bits can express 64 values, hence the name and the 64-character alphabet:
Work through the word Man:
01001101 01100001 01101110.010011 010110 000101 101110.Man encodes to TWFu. Three bytes in, four characters out, and each output character carries six bits where the input carried eight. That ratio is exactly where the 33% size increase comes from: 900 bytes of input produce 1,200 characters of output.
→ Use our free Base64 Encoder at GlobalUtilityHub to encode or decode any string instantly. No sign-up needed.
Padding, and why you see equals signs
The three-byte grouping is clean when your data divides by three. Most data does not.
When the final group has only one or two bytes, base64 pads the bits out to a full six-bit boundary with zeros, then appends equals signs so the decoder knows how many bytes were really there.
| Input | Bytes | Output | Padding |
|---|---|---|---|
| `Man` | 3 | `TWFu` | none |
| `Ma` | 2 | `TWE=` | one `=` |
| `M` | 1 | `TQ==` | two `=` |
The rule is simple. One equals sign means the last group held two bytes. Two equals signs mean it held one. You will never see three, because a group of three bytes needs no padding at all.
This is why base64 output length is always a multiple of four, and why SGVsbG8sIFdvcmxkIQ== ends the way it does: "Hello, World!" is 13 bytes, which is four groups of three with one byte left over.
Some systems strip the padding, on the grounds that a decoder can work out the original length from the number of characters. That is true, but not every decoder implements it. If you are getting an "invalid length" error on a base64 string, missing padding is the first thing to check.
base64 and base64url are not the same
Two characters in the standard alphabet cause trouble on the web: + and /.
In a URL query string, + conventionally means a space. And / is a path separator. A base64 string containing either will be mangled by anything that parses the URL, silently and without an error.
RFC 4648 defines a variant that fixes this. Everything is identical except two substitutions:
| Standard base64 | base64url |
|---|---|
| `+` | `-` |
| `/` | `_` |
| padding kept | padding usually dropped |
The difference is easy to see with data that produces both problem characters. Three specific bytes that encode to +/++ in standard base64 encode to -_-- in base64url. Same data, same 24 bits, different characters on the wire.
This matters because JSON Web Tokens use base64url, not base64. If you paste a JWT segment into a standard base64 decoder it may fail or return garbage, and the reason is almost always one of those two characters. Our guide on how to decode and debug a JWT covers where this bites in practice.
Where base64 belongs, and where it does not
Good uses:
Email attachments. MIME has used base64 since the early 1990s, and it remains the reason you can send a photograph through a protocol designed for plain text.
Data URIs. Embedding a small icon directly in CSS as data:image/png;base64,... removes an HTTP request. Worth it for small assets, not for large ones, because the 33% size penalty applies and the data cannot be cached separately.
JWTs and API tokens. The header and payload segments are base64url encoded so the token can travel in a URL or an HTTP header without escaping.
Binary in JSON. JSON has no binary type, so a file being sent inside a JSON body has to be encoded as a string somehow, and base64 is the convention.
Poor uses:
Storing images in a database. The 33% overhead applies to every row, indexes get larger, and you lose the ability to serve the file directly. Store a path and keep the file on disk or in object storage.
Anything you want kept private. This is the one that matters, and it deserves its own section.
Base64 is not encryption
This is the most common and most consequential misunderstanding about base64.
Encryption requires a key. Without the key, the original data cannot be recovered. Base64 has no key. The algorithm is public, the alphabet is fixed, and reversing it is a mechanical lookup that any decoder performs in microseconds.
A base64 string is not obscured. It is simply written in a different alphabet, in the same way that Morse code is not a secret language.
Every so often a credentials leak turns out to be a password that someone base64 encoded and considered handled. It was never handled. Anyone who obtains the string obtains the password immediately, with no effort and no tools beyond a browser console.
The related trap is HTTP Basic authentication. It transmits credentials as base64, which looks reassuringly opaque and is not. Basic auth is only safe over TLS, because TLS is doing all the actual protecting. The base64 is there to make the credentials safe to transmit as text, not safe from being read.
If you need data kept private, use encryption. If you need to detect tampering, use a signature or an HMAC. Base64 does neither and was never intended to.
How to decode base64 in practice
You rarely need to implement base64 yourself. Every mainstream environment has it built in, and knowing the one-liner for whichever you are in saves a trip to a website.
Command line, macOS and Linux
The -n on the encode side matters. Without it, echo appends a newline, that newline becomes part of the input, and you get TWFuCg== instead of TWFu. This is a common source of confusion when a manually generated token does not match one produced by an application.
JavaScript, browser and Node
These two only handle Latin-1 characters, so anything outside that range throws an error. For arbitrary text, encode to UTF-8 bytes first with TextEncoder. In Node, Buffer.from(str).toString("base64") handles this correctly without the extra step.
Python
Python operates on bytes rather than strings, so .encode() your text going in and .decode() the result coming out. The urlsafe_ variants handle the - and _ substitution automatically.
PHP
Java
A caution about online decoders. Pasting a value into a website sends it to that website's server. That is fine for debugging a public API response and a genuine mistake for anything containing credentials, personal data, or a production token. Use a local command or a tool that runs entirely in your browser. If you cannot tell which a given site is, assume the worst.
The bottom line
Base64 solves a transport problem: getting arbitrary bytes through channels that only handle text. It does that job reliably, at a predictable 33% cost in size, using an alphabet every system agrees on.
Remember three things. The equals signs are padding that tells the decoder how many real bytes the last group held. The URL-safe variant swaps two characters and is what JWTs use. And base64 offers no security at all, so nothing sensitive should ever rely on it for protection.
Our Base64 Encoder handles both directions in under 30 seconds. Try it free at globalutilityhub.com/dev-tools/base64-encoder/
Use our free Base64 Encoder to apply what you have learned.
Open Base64 Encoder →