The 64 characters
Base64 reads the input 3 bytes (24 bits) at a time and splits them into four 6-bit numbers. Each number, from 0 to 63, is an index into this table. The order is fixed by RFC 4648: uppercase letters for 0 to 25, lowercase for 26 to 51, digits for 52 to 61, then + and /. The last column is the ASCII code of the character, which is what actually gets stored or sent.
| Index | 6 bits | Base64 | Base64URL | ASCII code |
|---|---|---|---|---|
| 0 | 000000 | A | A | 0x41 |
| 1 | 000001 | B | B | 0x42 |
| 2 | 000010 | C | C | 0x43 |
| 3 | 000011 | D | D | 0x44 |
| 4 | 000100 | E | E | 0x45 |
| 5 | 000101 | F | F | 0x46 |
| 6 | 000110 | G | G | 0x47 |
| 7 | 000111 | H | H | 0x48 |
| 8 | 001000 | I | I | 0x49 |
| 9 | 001001 | J | J | 0x4A |
| 10 | 001010 | K | K | 0x4B |
| 11 | 001011 | L | L | 0x4C |
| 12 | 001100 | M | M | 0x4D |
| 13 | 001101 | N | N | 0x4E |
| 14 | 001110 | O | O | 0x4F |
| 15 | 001111 | P | P | 0x50 |
| 16 | 010000 | Q | Q | 0x51 |
| 17 | 010001 | R | R | 0x52 |
| 18 | 010010 | S | S | 0x53 |
| 19 | 010011 | T | T | 0x54 |
| 20 | 010100 | U | U | 0x55 |
| 21 | 010101 | V | V | 0x56 |
| 22 | 010110 | W | W | 0x57 |
| 23 | 010111 | X | X | 0x58 |
| 24 | 011000 | Y | Y | 0x59 |
| 25 | 011001 | Z | Z | 0x5A |
| 26 | 011010 | a | a | 0x61 |
| 27 | 011011 | b | b | 0x62 |
| 28 | 011100 | c | c | 0x63 |
| 29 | 011101 | d | d | 0x64 |
| 30 | 011110 | e | e | 0x65 |
| 31 | 011111 | f | f | 0x66 |
| 32 | 100000 | g | g | 0x67 |
| 33 | 100001 | h | h | 0x68 |
| 34 | 100010 | i | i | 0x69 |
| 35 | 100011 | j | j | 0x6A |
| 36 | 100100 | k | k | 0x6B |
| 37 | 100101 | l | l | 0x6C |
| 38 | 100110 | m | m | 0x6D |
| 39 | 100111 | n | n | 0x6E |
| 40 | 101000 | o | o | 0x6F |
| 41 | 101001 | p | p | 0x70 |
| 42 | 101010 | q | q | 0x71 |
| 43 | 101011 | r | r | 0x72 |
| 44 | 101100 | s | s | 0x73 |
| 45 | 101101 | t | t | 0x74 |
| 46 | 101110 | u | u | 0x75 |
| 47 | 101111 | v | v | 0x76 |
| 48 | 110000 | w | w | 0x77 |
| 49 | 110001 | x | x | 0x78 |
| 50 | 110010 | y | y | 0x79 |
| 51 | 110011 | z | z | 0x7A |
| 52 | 110100 | 0 | 0 | 0x30 |
| 53 | 110101 | 1 | 1 | 0x31 |
| 54 | 110110 | 2 | 2 | 0x32 |
| 55 | 110111 | 3 | 3 | 0x33 |
| 56 | 111000 | 4 | 4 | 0x34 |
| 57 | 111001 | 5 | 5 | 0x35 |
| 58 | 111010 | 6 | 6 | 0x36 |
| 59 | 111011 | 7 | 7 | 0x37 |
| 60 | 111100 | 8 | 8 | 0x38 |
| 61 | 111101 | 9 | 9 | 0x39 |
| 62 | 111110 | + | - | 0x2B |
| 63 | 111111 | / | _ | 0x2F |
| padding | – | = | usually omitted | 0x3D |
Two ranges are worth memorizing. Every character from A to P (0 to 15) starts with the bits 00, and every character from w to / (48 to 63) starts with 11. That is enough to decode a character in your head when you are checking a prefix by hand. A worked example of the bit splitting is in how Base64 works.
The alphabet order is not ASCII order. In ASCII, + and / come first, then digits, then uppercase, then lowercase. Sorting Base64 strings therefore does not sort the underlying bytes: +A== (the byte F8) sorts before AA== (the byte 00). If you need encoded values that sort like the raw bytes, use base32hex from RFC 4648 or plain hex.
The URL-safe alphabet
+ and / both have special meaning in URLs. + decodes to a space in query strings and form data, and / separates path segments. Base64URL, defined in section 5 of RFC 4648, keeps indexes 0 to 61 and swaps the last two:
| Index | Base64 | Base64URL |
|---|---|---|
| 62 | + | - |
| 63 | / | _ |
Everything else, including the bit layout, is identical, so converting between them is a character replacement plus adding or removing padding. JWTs, many API tokens and file-name-safe identifiers use Base64URL, usually without =. The Base64URL encoder produces it directly. The full comparison is in Base64 vs Base64URL.
The padding character
= (ASCII 0x3D) is not part of the 64-character alphabet and never encodes data. It only fills the last 4-character group when the input length is not a multiple of 3:
- 1 byte left over: 2 data characters plus
==. - 2 bytes left over: 3 data characters plus
=. - No bytes left over: no padding.
So a padded string always has a length that is a multiple of 4, and you never see ===. Without padding, the length divided by 4 leaves 0, 2 or 3, never 1. The padding guide covers when = may be dropped and how to restore it.
Which characters appear where
A few patterns follow directly from the bit layout. They were checked by brute force over all relevant byte values.
Before == you only see A, Q, g or w. With one byte left over, the second character carries 2 data bits followed by 4 zero bits, so its index is 0, 16, 32 or 48. Before a single =, the third character has 2 zero bits at the end, so it is one of 16 characters: A E I M Q U Y c g k o s w 0 4 8. Anything else in that position means a non-canonical encoding, which strict decoders reject.
Random bytes use all 64 characters equally. Each 6-bit group of random data is uniformly distributed, so encrypted data, keys and compressed files produce a flat character distribution. A skewed distribution is a hint that the data is text or has structure.
ASCII text never puts lowercase g to z, digits, + or / first. The first character of each 4-character group holds the top 6 bits of a byte. For 7-bit ASCII the top bit is 0, so that character is always between A and f (index 0 to 31). In SGVsbG8gd29ybGQh (Hello world!) the group starts are S, b, d and b.
In Base64 of ASCII text, + and / are rare. For bytes below 0x80, + can only appear as the fourth character of a group, when the third byte is > or ~. / can only appear there when that byte is ? or the DEL control character. That is why Hi?> encodes to SGk/Pg== and <b> to PGI+.
Runs of A are zero bytes, runs of / are 0xFF. AAAA is three 00 bytes, //// is three FF bytes. Long stretches of A are typical for sparse binary formats, UTF-16 text (Hello is SABlAGwAbABvAA==) and the length fields at the start of SSH keys. More recognizable patterns are listed in common Base64 prefixes.
Other Base64 alphabets
Several systems kept the idea of 64 characters per 6 bits but chose different characters:
| Variant | Alphabet in index order | Where |
|---|---|---|
| Standard | A-Z a-z 0-9 + / | RFC 4648, MIME, PEM, most APIs |
| URL-safe | A-Z a-z 0-9 - _ | RFC 4648 section 5, JWT |
| IMAP mailbox names | A-Z a-z 0-9 + , | RFC 3501 modified UTF-7 |
| bcrypt | . / A-Z a-z 0-9 | $2a$, $2b$, $2y$ password hashes |
| crypt(3) | . / 0-9 A-Z a-z | MD5-crypt $1$, SHA-crypt $5$ and $6$, DES crypt |
| xxencode | + - 0-9 A-Z a-z | Old Unix mail and Usenet |
| uuencode | ASCII 32 to 95 (space to _), often with ` for 0 | Old Unix mail and Usenet |
- IMAP replaces
/with,because/is a common hierarchy separator in mailbox names. A folder calledEntwürfeis stored asEntw&APw-rfe. - bcrypt moves
.and/to the front and keeps letters before digits. Its salts and hashes are Base64 in this alphabet without padding, which is why a standard decoder gives the wrong bytes for them. - crypt(3) uses
./0-9A-Za-z, which is ASCII order. It also packs bits starting from the least significant end, so even with a translated alphabet a standard decoder will not reproduce the hash bytes. - uuencode and xxencode predate MIME. The uuencode and xxencode tools read and write both.
For encodings with different alphabet sizes, such as Base32, Base58 and Base85, see Base64 vs hex, Base32, Base58 and Base85. Definitions of the terms used here are in the glossary.
FAQ
How many characters are in the Base64 alphabet?
64 characters for data: 26 uppercase letters, 26 lowercase letters, 10 digits, + and /. The padding sign = is a 65th symbol that never carries data.
What is the Base64 value of the letter A?
A stands for index 0, binary 000000. B is 1, Z is 25, a is 26, z is 51, 0 is 52, 9 is 61, + is 62 and / is 63.
Which characters does Base64URL change?
Only two: + (index 62) becomes - and / (index 63) becomes _. Padding is usually dropped as well. Convert with Base64 to Base64URL.