Timeline
| Year | Document | What it contributed |
|---|---|---|
| 1987 | RFC 989 (PEM, part I) | The 64-character “printable encoding” with = padding, plus * for unencrypted sections |
| 1988, 1989 | RFC 1040, RFC 1113 | Revised PEM drafts |
| 1992 | RFC 1341 (MIME) | Base64 as a Content-Transfer-Encoding for email |
| 1993 | RFC 1421 (PEM), RFC 1521 (MIME) | PEM lines of exactly 64 characters, revised MIME |
| 1996 | RFC 2045 (MIME) | The MIME definition still in use: lines of at most 76 characters |
| 1997 | RFC 2152 (UTF-7) | Base64 inside a text encoding for Unicode |
| 2003 | RFC 3501 (IMAP4rev1) | Modified Base64 with , for mailbox names |
| 2003 | RFC 3548 | First standalone spec: base64, base64url, base32, base16 |
| 2006 | RFC 4648 | Replaces RFC 3548, adds base32hex and test vectors |
| 2007 | RFC 4880 (OpenPGP) | Radix-64 armor with a CRC-24 checksum |
| 2015 | RFC 7468 | Strict rules for PEM-style certificate and key files |
| 2015 | RFC 7515 (JWS) | Base64URL without padding for tokens |
| 2024 | RFC 9580 (OpenPGP) | Replaces RFC 4880, deprecates the armor checksum |
PEM: where Base64 started
The alphabet we use today first appeared in RFC 989, “Privacy Enhancement for Internet Electronic Mail”, published in February 1987. Encrypted mail had to pass through gateways that only handled printable text, so PEM needed a way to write binary ciphertext with characters that survive any mail system. It took 6 bits at a time and mapped them to A-Z, a-z, 0-9, + and /, with = for padding. The table in RFC 989 is identical to the alphabet table on this site. It also reserved * to mark sections that were not encrypted, a feature that did not survive.
PEM went through RFC 1040 and RFC 1113 and settled in RFC 1421 (February 1993), which fixed the line length: “each line except the last containing exactly 64 printable characters and the final line containing 64 or fewer”. The reason given was printing and SMTP’s 1000-character line limit. PEM as a secure mail system never caught on, but its file format did. Every -----BEGIN CERTIFICATE----- block is a descendant, and RFC 7468 (2015) now spells out the rules: generators must wrap at exactly 64 characters, parsers may accept other widths. Inspect such files with the certificate decoder.
MIME: Base64 for every email attachment
MIME (RFC 1341 in 1992, RFC 1521 in 1993, RFC 2045 in 1996) needed a way to put images and documents into email and borrowed PEM’s encoding. RFC 2045 is still the reference for email today, and it differs from PEM in two ways:
- Encoded lines must not be longer than 76 characters, separated by CRLF.
- “Any characters outside of the base64 alphabet are to be ignored in base64-encoded data.” Decoders skip line breaks, spaces and anything else that is not part of the alphabet.
That second rule is why so many decoders silently drop invalid characters. Generate MIME-style output with the MIME Base64 encoder, and see Base64 in email for how attachments are structured.
RFC 3548 and RFC 4648: one reference for all
Until 2003, every protocol that used Base64 pointed to MIME or PEM and inherited their line-length and lenient-decoding rules, whether it wanted them or not. RFC 3548 (July 2003) defined base64, base64url, base32 and base16 as standalone encodings. RFC 4648 (October 2006) replaced it with minor changes: it added base32hex, which keeps the sort order of the encoded data, and a set of test vectors:
BASE64("") = ""
BASE64("f") = "Zg=="
BASE64("fo") = "Zm8="
BASE64("foo") = "Zm9v"
BASE64("foobar") = "Zm9vYmFy"
The rules in RFC 4648 are stricter than MIME’s, unless the specification that references it says otherwise:
- Line feeds: implementations must not add them (section 3.1).
- Padding: implementations must include
=padding (section 3.2). - Non-alphabet characters: decoders must reject them (section 3.3).
- Canonical encoding: encoders must set unused bits to zero, and decoders may reject input where they are not (section 3.5). Without this,
SGk/Pg==andSGk/Ph==both decode toHi?>.
The phrase “unless the specification referring to this document explicitly states otherwise” is doing a lot of work. It is how JWS gets to drop padding and MIME gets to keep its 76-character lines while both still use “RFC 4648 Base64”. The Base32 and Base16 tools implement the other two encodings from the same RFC.
Variants defined in other standards
JWS and JWT: Base64URL without padding
RFC 7515 (May 2015) defines BASE64URL(x) as the section 5 alphabet “with all trailing ‘=’ characters omitted”, with no line breaks or whitespace. JWT, JWE, JWK and most of the JOSE family use this. Details in Base64 in JWT.
IMAP: modified UTF-7 with a comma
IMAP mailbox names are 7-bit strings, so RFC 3501 (section 5.1.3) encodes non-ASCII folder names in “modified UTF-7”. Runs of non-ASCII characters are converted to UTF-16, Base64-encoded without padding, with , instead of /, and wrapped in & and -. The RFC’s own example &U,BTFw- is the Chinese name 台北, and a German folder Entwürfe becomes Entw&APw-rfe. Plain UTF-7 (RFC 2152) writes the same name as Entw+APw-rfe.
OpenPGP: Radix-64 with a CRC-24
OpenPGP’s ASCII armor is MIME Base64 plus a checksum line. RFC 4880 defines a CRC-24 (generator 0x864CFB, initial value 0xB704CE) over the binary data, Base64-encoded and prefixed with =. GnuPG produces this for the 12 bytes Hello world!:
-----BEGIN PGP ARMORED FILE-----
Comment: Use "gpg --dearmor" for unpacking
SGVsbG8gd29ybGQh
=s4Gu
-----END PGP ARMORED FILE-----
RFC 4880 allows lines of up to 76 characters; GnuPG writes 64. RFC 9580 (July 2024) keeps the format but says the CRC-24 line “SHOULD NOT be generated” and that implementations must not reject a block because it is missing or wrong.
Table of Base64 variants
| Variant | Index 62 and 63 | Padding | Line length | Defined in |
|---|---|---|---|---|
| Standard Base64 | + / | Required | None | RFC 4648, section 4 |
| Base64URL | - _ | Optional, by referring spec | None | RFC 4648, section 5 |
| JWS / JWT | - _ | Never | None | RFC 7515 |
| MIME | + / | Required | At most 76, CRLF | RFC 2045 |
| PEM | + / | Required | Exactly 64 | RFC 1421, RFC 7468 |
| OpenPGP armor | + / | Required | At most 76 (GnuPG: 64) | RFC 4880, RFC 9580 |
| UTF-7 | + / | None | Not applicable | RFC 2152 |
| IMAP modified UTF-7 | + , | None | Not applicable | RFC 3501 |
| bcrypt | Alphabet ./A-Za-z0-9 | None | Not applicable | De facto (OpenBSD) |
| crypt(3) hashes | Alphabet ./0-9A-Za-z | None | Not applicable | De facto (Unix) |
In the last two rows the whole alphabet is reordered. They are covered on the alphabet page. Many other formats reference one of the rows above without changing anything: HTTP Basic authentication (RFC 7617), data URIs (RFC 2397), XML Schema’s base64Binary, and Subresource Integrity hashes all use standard Base64 with padding.
FAQ
Which RFC defines Base64?
RFC 4648 (October 2006) is the general reference. It defines base64, base64url, base32, base32hex and base16. MIME’s version is in RFC 2045, and PEM’s original in RFC 1421.
Why are MIME lines 76 characters and PEM lines 64?
Both limits keep lines well under SMTP’s 1000-character limit. PEM fixed 64 in RFC 1421; MIME chose “no more than 76” in its own spec. RFC 4648 says encoders must not add line breaks at all unless the referring standard asks for them.
Is Base64URL an official standard?
Yes. It is section 5 of RFC 4648, “Base 64 Encoding with URL and Filename Safe Alphabet”, and appeared first in RFC 3548. JWS (RFC 7515) uses it without padding.
What is the =XXXX line at the end of a PGP block?
A CRC-24 checksum of the decoded data, itself written in Base64. RFC 9580 (2024) says new implementations should not emit it and must not reject a block because of it.