Base64 encoding is a scheme that represents arbitrary binary data using only 64 printable ASCII characters. It takes three bytes at a time, splits those 24 bits into four 6-bit groups, and maps each group to one text character, making binary safe to move through text-only channels.
If you have ever peeked at an email attachment’s raw source, decoded a JSON Web Token, or embedded a small image directly inside a stylesheet, you have already met base64. It is one of the quiet workhorses of the web: rarely discussed, constantly used. This guide explains exactly what base64 is, how the transformation works byte by byte, where it genuinely helps, and the single most important thing developers get wrong about it, that base64 is a format, not a lock.
What is base64 encoding, really?
Computers store everything as bytes, and a byte can hold any of 256 values (0 to 255). Plenty of those values are not printable text: control characters, null bytes, and other patterns that older protocols were never designed to carry. If you drop raw binary into an email body, an HTTP header, or a URL, something along the way may mangle it.
Base64 solves this by re-expressing binary data using a small, safe alphabet of 64 characters that survive almost any text channel. The name comes from that count: it is a positional numbering system with a base, or radix, of 64. The output is still text, but it can faithfully reconstruct the original bytes when decoded. In short, base64 is a translation layer between the byte world and the text world, and it is fully reversible.
The 64-character alphabet
The standard alphabet, defined in RFC 4648, assigns an index from 0 to 63 to each character:
- 0-25 map to uppercase
AthroughZ - 26-51 map to lowercase
athroughz - 52-61 map to the digits
0through9 - 62 maps to
+ - 63 maps to
/
That is 64 symbols in total. A 65th character, the equals sign =, is reserved for padding and is not part of the value alphabet. It carries no data of its own; it only rounds out the length.
How does base64 work?
The core trick is a change of grouping. Ordinary bytes are 8 bits each. Base64 ignores the 8-bit boundary and instead reads the same stream of bits in 6-bit chunks, because 6 bits can represent exactly 64 values, one for each character in the alphabet.
The math lines up neatly: the lowest common multiple of 8 and 6 is 24 bits. So base64 processes input in blocks of 3 bytes (3 x 8 = 24 bits), and each block becomes 4 output characters (4 x 6 = 24 bits). Three bytes in, four characters out, every time.
A worked example, step by step
Take the three-letter string Man. In ASCII, those characters are the byte values 77, 97, and 110. Written in binary, the 24 bits are:
01001101 01100001 01101110
Now regroup the identical bits into four 6-bit pieces instead of three 8-bit ones:
010011 010110 000101 101110
Convert each 6-bit group to a decimal index, then look it up in the alphabet:
| 6-bit group | Decimal value | Base64 character |
|---|---|---|
| 010011 | 19 | T |
| 010110 | 22 | W |
| 000101 | 5 | F |
| 101110 | 46 | u |
So Man encodes to TWFu. Decoding simply reverses the process: map each character back to its 6-bit value, concatenate the bits, and re-slice them into 8-bit bytes.
What about padding and the = sign?
Real data is rarely a tidy multiple of 3 bytes, so base64 needs a rule for the leftovers. When the final block has only 1 or 2 bytes, the encoder pads the missing bits with zeros to complete a 6-bit group, then appends = characters so the total output is still a multiple of 4 characters.
- Two leftover bytes produce three real characters plus one
=. Example:MabecomesTWE=. - One leftover byte produces two real characters plus two
=. Example:MbecomesTQ==. - No leftovers means no padding at all, as with
Manabove.
The padding is a signal to the decoder about how many bytes the last block actually held, so it knows to discard the zero-filled bits rather than treat them as data.
When should you use base64?
Base64 earns its keep whenever binary data has to pass through a channel that expects text, or when embedding small binary blobs inline is more convenient than a separate file or request. Common, legitimate uses include:
- Email attachments (MIME). The SMTP email system was built for 7-bit text. MIME uses base64 to carry images, PDFs, and other binaries inside message bodies without corruption.
- Data URIs. You can inline an image or font directly in HTML or CSS with
data:image/png;base64,iVBORw0KGgo..., avoiding an extra HTTP request for a tiny asset. - JSON and XML payloads. These formats are text-only, so binary fields (a thumbnail, a signature, a file blob) are commonly base64-encoded before being placed in a string value.
- JSON Web Tokens (JWT). Each of a JWT’s three parts, the header, the payload, and the signature, is base64url-encoded and joined with dots. This makes the token compact and URL-safe, not secret.
- HTTP Basic authentication. The
Authorizationheader sendsusername:passwordas a base64 string. This is purely for safe transport of characters; it offers no protection, which is exactly why Basic auth must run over HTTPS. - Storing keys and certificates. PEM files wrap DER binary in base64 between
-----BEGIN-----and-----END-----markers so they can be copied and pasted as text.
The URL-safe variant: base64url
Standard base64 uses + and /, but both cause problems inside URLs, query strings, and filenames, where they have special meaning or are illegal. RFC 4648 defines a URL-safe variant, base64url, with two swaps:
| Purpose | Standard base64 | base64url |
|---|---|---|
| Index 62 | + | – |
| Index 63 | / | _ |
| Padding | = (required) | = (often omitted) |
The decoded bytes are byte-for-byte identical; only two alphabet characters and the padding convention change. JWTs, OAuth 2.0 PKCE codes, and WebAuthn challenges all use base64url, and the JWT specification forbids padding entirely.
Is base64 secure or a form of encryption?
No. This is the single most important point in this article, so it deserves to be blunt: base64 is encoding, not encryption. It provides no security, no confidentiality, and no integrity protection whatsoever.
Encryption relies on a secret key: without the key, ciphertext is unreadable. Base64 has no key. The algorithm is public, fixed, and reversible by anyone in a fraction of a second. A base64 string is not scrambled or protected; it is merely rewritten in a different alphabet. Reading it back is not “cracking” anything, it is just decoding, which is a normal, expected operation.
A quick way to internalize the difference:
| Property | Base64 encoding | Encryption |
|---|---|---|
| Requires a secret key | No | Yes |
| Reversible by anyone | Yes, instantly | No, only with the key |
| Goal | Safe transport of bytes as text | Confidentiality of data |
| Hides the content | No | Yes |
The practical takeaway: never base64-encode a password, API key, token, or any secret and call it “protected.” If you see credentials stored or transmitted as base64 and nothing else, treat them as plaintext, because effectively they are. When you need real protection, use encryption (such as AES for data at rest, or TLS for data in transit) and, where appropriate, hashing for values like stored passwords. You can, of course, base64-encode data that has already been encrypted; that is common and fine, because the security comes from the encryption step, not from base64.
What are the downsides of base64?
Base64 is useful, but it is not free. Keep these trade-offs in mind before reaching for it:
- Roughly 33% size increase. Because 3 bytes become 4 characters, encoded output is about one-third larger than the original. Inserting line breaks (as MIME does) adds a few percent more. For large payloads this bloat matters for bandwidth and storage.
- Processing overhead. Encoding and decoding cost CPU time and memory. For big files, streaming binary directly is usually faster and lighter than round-tripping through base64.
- Not human-readable. The output looks like noise, so it hides nothing useful from a human but also communicates nothing at a glance.
- Poor fit for large inline assets. A giant base64 data URI embedded in HTML or CSS cannot be cached separately by the browser and inflates the containing document. Inlining is best reserved for small assets.
- A false sense of security. As covered above, mistaking base64 for protection is a genuine and recurring vulnerability, not a theoretical one.
A quick mental model
Think of base64 like packing fragile goods for shipping through a mail system that only accepts flat envelopes. You are not locking the contents away from prying eyes; you are just repackaging them into a shape the delivery system can handle without breaking anything. The package is bigger than the bare item, anyone can open it, and the moment it arrives you unpack it back to the original. That is base64 in one sentence: a reliable, reversible repackaging of bytes into text.
Key takeaways
- Base64 represents binary data using 64 printable characters, converting 3 bytes into 4 characters at a time.
- The standard alphabet is
A-Z,a-z,0-9,+, and/, with=used only for padding. - Encoded output is about 33% larger than the input, so it is not ideal for very large data.
- Base64url swaps
+and/for-and_and usually drops padding, for use in URLs, filenames, and JWTs. - Most importantly, base64 is encoding, not encryption. It offers no security and can be decoded by anyone. Use real encryption when you need to protect data.