Upload a file
  1. base64hub
  2. Learn Base64
  3. How Base64 works
Explainer

How Base64 Works, Byte by Byte

Base64 rewrites any bytes using 64 safe characters. Three bytes go in, four characters come out, and the rest is bookkeeping.

What Base64 is and why it exists

Base64 is a way to write arbitrary binary data using only 64 printable ASCII characters: A–Z, a–z, 0–9, + and /, plus = for padding. It is an encoding, which means anyone can reverse it without a key. It hides nothing (more on that in encoding vs encryption).

The goal is to get bytes through text-only channels intact. A PNG file contains bytes like 0x00, 0x1A and 0x89 that a text channel may strip, translate or treat as a control code. Written as Base64, the same file becomes iVBORw0KGgo..., which passes through email, JSON, XML, URLs (with the URL-safe variant), terminals and copy-paste unchanged.

The current definition is RFC 4648. Older specs for MIME email (RFC 2045) and PEM certificates describe the same alphabet with extra rules about line length. A summary of the variants lives on the Base64 standards page.

Why email needed it

Early email was designed for 7-bit ASCII text. Mail relays of the time could strip the high bit of each byte, wrap long lines and rewrite line endings, which is harmless for a letter and fatal for a ZIP file. MIME needed a way to carry any attachment through that pipe, and it needed an alphabet that survives every system along the way.

Base64 picks its 64 characters for that reason. Letters and digits are identical in ASCII and in nearly every legacy character set, + and / are almost as safe, and none of them are control characters or whitespace. 64 is also the largest power of two you can fill from that safe set, and a power of two keeps the bit arithmetic simple. The cost is size: 6 useful bits per 8-bit character means the output is 4/3 of the input, plus padding.

The 8-bit problem is mostly gone from modern mail servers, but the same constraint shows up anywhere binary data has to sit inside a text format. JSON has no binary type, and HTML attributes, CSS and HTTP headers are all text, so Base64 is still the default way to put bytes into them.

The algorithm: 3 bytes in, 4 characters out

Base64 works on blocks of 24 bits. That number is the smallest one divisible by both 8 (a byte) and 6 (one Base64 character), so 3 bytes map exactly onto 4 characters.

For each block:

  1. Take 3 bytes and write them as 24 bits in a row.
  2. Cut those 24 bits into four groups of 6 bits.
  3. Read each group as a number from 0 to 63.
  4. Look the number up in the alphabet and output that character.

The alphabet is a fixed table. Index 0 is A, 25 is Z, 26 is a, 51 is z, 52 is 0, 61 is 9, 62 is + and 63 is /. The full table is on the Base64 alphabet page.

RangeCharactersExample
0–25A–Z19 → T
26–51a–z46 → u
52–610–952 → 0
62+
63/

Decoding runs the same steps backwards: look each character up to get 6 bits, join four of them into 24 bits, and split those into 3 bytes.

Worked example: “Man” becomes TWFu

The classic example uses the ASCII text Man. In ASCII, M is 77, a is 97 and n is 110.

Man
Decimal7797110
Binary010011010110000101101110

Join the bits: 010011010110000101101110. Now cut them every 6 bits instead of every 8:

6-bit group010011010110000101101110
Value1922546
CharacterTWFu

So Man encodes to TWFu. Notice that no output character lines up with an input letter. The W is built from the last 4 bits of M and the first 2 bits of a. This is why changing one byte of input changes two characters of output, and why you cannot read Base64 by eye even when the input is plain English.

You can check it in any shell:

echo -n Man | base64
# TWFu

Without -n, echo adds a newline byte, and Man\n encodes to TWFuCg==.

When the input is not a multiple of 3: padding

Real data rarely has a length divisible by 3. The last block then has 1 or 2 bytes, and Base64 fills it out with zero bits and = signs.

Two bytes left, “Ma” → TWE=. Two bytes are 16 bits. Add two zero bits to reach 18, which is three 6-bit groups:

6-bit group010011010110000100(none)
Value19224
CharacterTWE=

The third group is 0001 from a followed by the two added zeros, giving 4, which is E. One = stands in for the missing fourth character.

One byte left, “M” → TQ==. One byte is 8 bits. Add four zero bits to reach 12, which is two groups: 010011 (19, T) and 010000 (16, Q). Two = signs fill the block.

So the number of = signs tells you how many bytes the last block held: none means 3, one means 2, two means 1. There is never a third =, because a block with zero leftover bytes does not exist. The padding guide covers when = can be dropped and how to put it back.

import base64
for s in [b"Man", b"Ma", b"M"]:
    print(s, base64.b64encode(s))
# b'Man' b'TWFu'
# b'Ma' b'TWE='
# b'M' b'TQ=='

Text has to become bytes first

Base64 encodes bytes. Text only becomes bytes once you pick a character encoding, and the choice changes the result. The word café in UTF-8 is 5 bytes (é takes two), so it encodes to Y2Fmw6k=. In Latin-1 it is 4 bytes and encodes to Y2Fm6Q==.

This is the source of a well-known JavaScript trap. The browser function btoa() treats each character of a string as one byte, which is Latin-1 behavior:

btoa("café");        // "Y2Fm6Q==" (Latin-1, not UTF-8)
btoa("café ✓");      // throws InvalidCharacterError

To get UTF-8 Base64 in JavaScript, encode to bytes first with TextEncoder, or use Buffer in Node.js. The JavaScript code page shows the working patterns, and Uint8Array.toBase64() covers the newer built-in method. The Base64 encoder on this site uses UTF-8 by default and lets you choose another charset.

Where you meet Base64

  • Email attachments. Every attachment in a raw .eml file is a Base64 block wrapped at 76 characters. See Base64 in email.
  • Data URIs. data:image/png;base64,iVBORw0KGgo... embeds an image directly in HTML or CSS. Whether that is a good idea is covered in data URI performance.
  • HTTP Basic Auth. The header Authorization: Basic dXNlcjpwYXNz is just user:pass in Base64.
  • JWTs. The three dot-separated parts of a JSON Web Token are Base64URL. See Base64 in JWT.
  • PEM files. Certificates and keys between -----BEGIN ...----- lines are Base64 of binary DER data.
  • APIs and config. Kubernetes Secrets, cloud metadata, webhook payloads and many JSON APIs carry files or binary blobs as Base64 strings.

After a while you start recognizing blobs by their first characters: iVBORw0KGgo is a PNG, JVBERi0 is a PDF, /9j/ is a JPEG and eyJ is JSON starting with {". The common Base64 prefixes page lists more. To see what a blob holds, the Base64 to file tool detects the format and previews it in your browser.

Decode a string now

Paste Base64 and see the text or bytes inside. It runs locally in your browser.

Base64 decoder

FAQ

Is Base64 a form of compression?

No. It always makes data larger: every 3 input bytes become 4 output characters, about 33% more. See Base64 size overhead for the exact formula.

Why is it called Base64?

Each output character carries one of 64 possible values, which is 6 bits of information. The name is the size of the alphabet, the same way hex is base 16.

Can any data be Base64 encoded?

Yes. The algorithm works on bytes and does not care what they mean, so text, images, PDFs, keys and compressed archives all encode the same way. Text must first be turned into bytes with a character encoding, usually UTF-8.

Does Base64 output ever contain spaces or line breaks?

Plain RFC 4648 Base64 does not. Some formats wrap it into lines, for example MIME email at 76 characters and PEM files at 64, and most decoders skip that whitespace.

Keep reading