Base64 decoding reverses the encoding scheme defined in RFC 4648 by mapping each character of the 64-symbol alphabet back to its 6-bit value, recombining those bits into 8-bit bytes, and stripping the '=' padding that fills out the final group. The alphabet is fixed and small enough to memorize: A–Z, a–z, 0–9, '+', '/', and '=' for padding — exactly 2⁶ = 64 symbols, which is the source of the name. Decoding is fully deterministic, so a string like 'SGVsbG8sIFdvcmxkIQ==' maps back to one and only one byte sequence, in this case the ASCII bytes for 'Hello, World!'. The complication almost every casual decoder gets wrong is Unicode. The browser's built-in helpers only accept Latin-1 code points, so 'café', '你好', or '😀' either throw an error or come back corrupted. A correct decoder routes through UTF-8 bytes — encode to UTF-8 first, then Base64 the bytes, then on decode reverse with a strict UTF-8 validator so malformed input is rejected instead of silently mangled. That single rule fixes more Base64 bugs than anything else.

The Base64 Alphabet at a Glance
RFC 4648 §4 defines exactly 64 printable characters, indexed 0 through 63. Index 0 is 'A', index 25 is 'Z', index 26 is 'a', index 51 is 'z', index 52 is '0', index 61 is '9', index 62 is '+', and index 63 is '/'. '=' is not part of the alphabet proper — it is the padding character used only at the end to round the output length up to a multiple of four. Memorizing the table below lets you decode short strings by hand: every four input characters turn back into three original bytes.
| Index | Char | Index | Char | Index | Char | Index | Char |
|---|---|---|---|---|---|---|---|
| 0 | A | 16 | Q | 32 | g | 48 | w |
| 1 | B | 17 | R | 33 | h | 49 | x |
| 2 | C | 18 | S | 34 | i | 50 | y |
| 3 | D | 19 | T | 35 | j | 51 | z |
| 4 | E | 20 | U | 36 | k | 52 | 0 |
| 5 | F | 21 | V | 37 | l | 53 | 1 |
| 6 | G | 22 | W | 38 | m | 54 | 2 |
| 7 | H | 23 | X | 39 | n | 55 | 3 |
| 8 | I | 24 | Y | 40 | o | 56 | 4 |
| 9 | J | 25 | Z | 41 | p | 57 | 5 |
| 10 | K | 26 | a | 42 | q | 58 | 6 |
| 11 | L | 27 | b | 43 | r | 59 | 7 |
| 12 | M | 28 | c | 44 | s | 60 | 8 |
| 13 | N | 29 | d | 45 | t | 61 | 9 |
| 14 | O | 30 | e | 46 | u | 62 | + |
| 15 | P | 31 | f | 47 | v | 63 | / |
Padding: the 65th symbol '=' (index "pad") is used only at the end to round the output length up to a multiple of four. URL-safe Base64 (RFC 4648 §5) replaces '+' with '-' and '/' with '_' so the output survives URL encoding — but the standard form above is the one you will meet most often.
How to Decode Base64 Online
For anything longer than a handful of characters, use the Base64 Encode / Decode tool. It runs entirely in your browser, applies the RFC 4648 alphabet and padding rules, and decodes via UTF-8 so emoji and accented characters round-trip correctly.
- Select Decode mode so the arrow points from Base64 → text.
- Paste the Base64 string into the top box. The decoded text appears in the bottom box as you type or paste — there is no submit button.
- If you need the result elsewhere, click Copy. If you want to re-encode the decoded text to verify the round-trip, click Swap to flip the direction without retyping.
Padding Rules and Length Math
Base64 groups three input bytes (24 bits) into four 6-bit characters (also 24 bits). When the input length is not a multiple of three, the last group is short, and '=' padding fills out the missing positions so the encoded length stays a multiple of four. That is why 'f' (one byte) becomes 'Zg==' — 'Zg' carries the only useful byte, then two '=' signs pad the final group to four characters. And 'fo' (two bytes) becomes 'Zm8=' — one '=' pad. Six bytes encode with no padding at all. The relationship between input size and output length is fixed; you can sanity-check any string with the table below.
| Input bytes | Output chars | Trailing padding |
|---|---|---|
| 1 | 4 | == |
| 2 | 4 | = |
| 3 | 4 | (none) |
| 4 | 8 | == |
| 5 | 8 | = |
| 6 | 8 | (none) |
| 3k + 0 | 4k | (none) |
| 3k + 1 | 4(k + 1) | == |
| 3k + 2 | 4(k + 1) | = |
The general formula is: for every 3 input bytes you get 4 output characters, with 1 '=' added when 1 byte remains and 2 '=' added when 2 bytes remain. The encoded length is therefore ⌈n/3⌉ × 4, where n is the input byte count. Reverse it: if you know only the encoded length, the original byte count is ⌊3m/4⌋ where m is the number of Base64 characters (excluding padding). This is how decoders estimate payload size from a JWT header without parsing it.
Quick Patterns That Identify a Base64 String
Valid Base64 has a very narrow shape, which makes it easy to recognize in logs and config files:
- Length is always a multiple of four (after padding is restored).
- Characters are limited to A–Z, a–z, 0–9, '+', '/' (or '-' and '_' in URL-safe form).
- The only valid trailing characters are '', '=', or '==' — never '===', and never a single '=' in the middle of the string.
- Line breaks appear every 60 or 76 characters in MIME-wrapped output, but they are stripped before decoding.
If you see whitespace mid-string, base64-decode implementations should ignore it. If you see any character outside the alphabet plus padding, the input is corrupted, truncated, or never was Base64 to begin with.
The UTF-8 Gotcha That Breaks Most Decoders
Base64 itself only knows about bytes — it has no concept of characters. The decoded result becomes meaningful text only after you decide which character encoding those bytes represent. For modern strings that means UTF-8: the bytes for 'é' are 0xC3 0xA9, the bytes for '你' are 0xE4 0xBD 0xA0, and the bytes for '😀' are 0xF0 0x9F 0x98 0x80. Encoding '😀' directly through the browser's built-in btoa() throws an error because btoa only accepts code points 0–255. Encoding it via the correct UTF-8 path produces 8 base64 characters: 8J+YgA==.
The reverse is just as fragile. A decoder that treats the byte stream as Latin-1 will turn 0xE4 into 'ä', which looks plausible but is wrong. A strict UTF-8 decoder rejects any byte sequence that is not valid UTF-8 instead of substituting the U+FFFD replacement character, so malformed input shows up as an error instead of silent corruption. Use a decoder that does this correctly — for example, the Base64 Encode / Decode tool encodes through TextEncoder and validates with a strict UTF-8 decoder.
If you need to convert the other direction — from Base64 bytes into hex, for instance — the Base64 to Hex converter handles the canonical padded form without losing leading zeros.
Where You'll Meet Base64 in the Wild
Base64 shows up wherever binary data has to ride a text-only channel. Five everyday places to recognize it:
- data: URIs. Inline images, fonts, and small assets embedded directly in HTML or CSS, prefixed with data:image/png;base64,.... The whole payload after the comma is one big Base64 string.
- MIME email attachments. SMTP bodies are 7-bit, so attachments are Base64-encoded and broken into 76-character lines.
- JSON Web Tokens. A JWT is three Base64URL-encoded segments separated by dots: header, payload, signature. Decode the payload to read the claims — never to trust them, since the signature is what proves authenticity.
- HTTP Basic auth. The Authorization header is literally 'Basic ' followed by Base64(username:password). It is encoded, not encrypted — every server admin can read it.
- API payloads. When a JSON or XML API needs to ship binary blobs (checksums, signatures, file previews) inside a text field, Base64 is the conventional carrier.
Base64 Is Encoding, Not Encryption
Base64 is fully reversible by design — that is the entire point. Anyone with the alphabet table can decode any string in seconds, so Base64 provides zero confidentiality. Treat it as safe transport of bytes across text-only channels, never as a way to hide passwords, API keys, or session tokens. If you see 'encrypted in Base64' in a product description, that vendor does not understand the difference.
For real secrecy you need a key-based primitive: AES-GCM, ChaCha20-Poly1305, or an asymmetric scheme like RSA-OAEP. And for cases where you want data to be unreadable to anyone with a copy of the bytes but do not need to decrypt it back, a one-way hash (SHA-256, SHA-512) is the right tool. Base64 sits next to all of these as a transport, not a substitute for any of them.
Decoding Worked Example
To make the mechanics concrete, here is one full round-trip you can verify by hand with the alphabet table above. Take the ASCII text 'f' — one byte, 0x66, binary 01100110.
- Pad the byte to 24 bits by appending sixteen zero bits: 01100110 00000000 00000000.
- Split into four 6-bit groups: 011001 100000 000000 000000.
- Convert each group to its decimal index: 25, 32, 0, 0.
- Look up each index in the alphabet: 25 = 'Z', 32 = 'g', 0 = 'A', 0 = 'A'.
- The last two characters are filler, so they become padding: 'Zg=='.
Now decode 'Zg==' back: read the alphabet — Z=25, g=32, pad, pad. Write the 6-bit values: 011001 100000. Concatenate to 12 bits: 011001100000. Split into bytes: 01100110 = 0x66 = 'f'. That is the original byte. The online decoder does this in microseconds and handles inputs of any length.
Quick Troubleshooting
When a decoded string looks almost right but contains weird characters, check these in order:
- Wrong alphabet. Standard Base64 uses '+' and '/'; URL-safe uses '-' and '_'. If the input came from a URL or filename, try the URL-safe form first.
- Padding stripped. Some transports (URLs, JSON fields) strip trailing '=' signs. Add them back until the length is a multiple of four.
- Newlines embedded. MIME-wrapped Base64 has line breaks every 76 characters. Most decoders strip them automatically; if yours does not, remove them by hand.
- Not actually Base64. Hex, Base32, Base58, and Base100 all look superficially similar — different alphabets, different rules. Confirm the alphabet before decoding.
- UTF-8 mismatch. If the decoded bytes look like Latin-1 gibberish with diacritics where you would expect accents, the original was not encoded as UTF-8.
Related reading: Base64 Hex Converter: Get Exact RFC 4648 Bytes.