Skip to content

Guide

Base64 Encoding Explained: Why, When, and the Common Variants

A 64-character alphabet, a 33% size penalty, and three variants that look almost identical until they break each other.

By Published

Base64 is everywhere — email attachments, JWT tokens, data URIs, certificate PEM blocks, OAuth client secrets, push notification payloads — and almost no one stops to ask what it’s actually doing. It does one specific job, has three near-identical variants, and the differences between them cause most of the bugs.

What problem Base64 solves

Many transport channels are designed for text, not arbitrary bytes. Email bodies historically had to be printable ASCII. URLs can’t contain raw control characters or spaces. JSON strings must be valid Unicode. If you want to send a PNG inside any of these, the raw bytes will break the channel.

Base64 takes any byte sequence and re-expresses it using only 64 safe characters plus optional padding. The result fits anywhere text fits, and a decoder recovers the original bytes exactly. There’s no loss, no compression, and no security — just a format change.

The alphabet

Standard Base64 (RFC 4648 §4) uses 65 characters total:

  • A–Z — values 0–25
  • a–z — values 26–51
  • 0–9 — values 52–61
  • + — value 62
  • / — value 63
  • = — padding, no value

Each character carries 6 bits (because 26 = 64). Three input bytes (24 bits) map to four output characters (24 bits). When the input isn’t a multiple of 3 bytes, the encoder zero-pads on the right and emits =for each padding step. One leftover byte becomes “two characters + ==”; two leftover bytes become “three characters + =”.

Why the output is 33% bigger

4 output characters carry only what 3 input bytes carried, so the inflation is exactly 4/3 = 1.333…, or 33.3% extra. Padding adds a tiny amount more for short inputs. A 1 MB binary file becomes a 1.33 MB Base64 string. This is the trade for text-safe transport; if you can’t afford it, you can’t use Base64.

Try our Base64 encoder / decoder with a sample input — you’ll see the size ratio reported alongside the result.

The three variants you’ll meet

Standard Base64 (RFC 4648 §4)

The original. Alphabet includes + and /, padding with =required. Used by PEM files (TLS certificates, SSH keys), XML Signature, SOAP attachments, and the Authorization: Basic HTTP header.

Base64url (RFC 4648 §5)

URL- and filename-safe. Replaces + with - and / with _. Padding is technically optional in the spec but almost always omitted in practice. This is what JWT, JWS, JWE, OAuth PKCE, WebPush VAPID, and WebAuthn use. The two characters that differ are the ones that previously needed percent-encoding inside URLs (%2B for + and %2F for /), and the missing padding sidesteps %3D as well.

Standard and URL-safe Base64 are not directly interchangeable. A decoder that knows one alphabet will reject input in the other. If you’re plumbing data between an OAuth flow and a tool that expects PEM-style Base64, transcode at the boundary. See our Base64 vs Base64url comparison for the exact rules.

MIME Base64 (RFC 2045)

The original email-attachment variant. Same alphabet as standard Base64, but with a hard line break (\r\n) inserted every 76 characters because old SMTP relays choked on long lines. Whitespace inside MIME Base64 must be ignored by the decoder. Most modern decoders accept whitespace silently in any Base64 input, but a strict RFC 4648 decoder is allowed to reject it. If you’re generating Base64 for a JSON field or a JWT, never insert line breaks.

Where Base64 shows up in the wild

  • JWT tokens — three base64url (unpadded) sections joined by dots. Header. Payload. Signature.
  • HTTP Basic auth username:password standard-Base64 encoded, sent in the Authorization header. (Why “Basic” is not a security mechanism without TLS.)
  • Data URIs data:image/png;base64,iVBORw0KG… embeds the binary directly in HTML or CSS. The image/png part is a MIME type — without it the browser has to sniff the bytes and may decline to render.
  • PEM files — TLS certificates, SSH keys, PGP messages. Standard Base64 wrapped in -----BEGIN…----- / -----END…----- headers.
  • Email attachments — MIME Base64 with 76-character line wrapping.
  • WebSocket frames over HTTP/2 some payloads use Base64 when binary frames aren’t available.
  • Configuration files — KubernetesSecretresources are standard-Base64 encoded values (which famously confuses people into thinking they’re encrypted).

Common pitfalls

Encoding strings without specifying UTF-8

Base64 doesn’t know about characters — it encodes bytes. If you Base64-encode the string “café”, the result depends entirely on which byte encoding you chose (UTF-8 gives 5 bytes producing Y2Fmw6k=; Latin-1 gives 4 bytes producing Y2Fm6Q==). Decoding under a different assumption gives garbage. The modern convention is UTF-8 bytes for any text input.

Mixing standard and URL-safe

A string containing - or _ is base64url; standard-Base64 decoders reject it. A string containing + or /is standard; URL-safe decoders reject it. If you control both ends, pick one and document it. If you don’t, normalise on input by character-class substitution before decoding.

Padding mismatch

JWTs and similar formats drop the padding. Naive decoders require padding and throw InvalidBase64. The fix is to re-append = characters until the input length is a multiple of 4 — usually one or two of them.

Treating Base64 as a security boundary

Kubernetes Secrets are Base64, not encrypted. Base64ing a password in client code doesn’t protect it. Base64 hides nothing from anyone with a decoder — and every laptop has one.

Data URI bloat

Inlining a 200 KB image with Base64 grows it to 266 KB and blocks the main thread while the browser parses the URI. For anything past a few KB, serve the file from a normal URL and let the browser cache it.

Try the encoder

Paste any text or upload a file in our Base64 encoder / decoder. It supports both standard and URL-safe alphabets, shows padding behaviour explicitly, and reports input/output byte sizes so you can see the inflation for your specific input. The Base64 glossary entry is the one-paragraph version.

Bottom line

Base64 is a transport convenience, not a security feature. It costs you a third of your bytes and pays you back in compatibility with text-only channels. The variants matter — standard for PEM and HTTP Basic, URL-safe for JWTs and OAuth, MIME for email — and they don’t decode each other without transcoding. Always be explicit about which variant you produce and consume, and always encode Unicode strings to UTF-8 bytes first.

Frequently asked questions

Is Base64 encryption?
No. Base64 is encoding, not encryption — there's no key, and anyone can reverse it instantly. It exists to make binary data safely transportable through text-only channels (email bodies, JSON, URLs). Treating Base64 as a security measure is a vulnerability, not a defence.
Why is the encoded output always longer than the input?
Each Base64 character carries only 6 bits of information; an input byte carries 8. So every 3 input bytes turn into 4 output characters — a 4/3 (≈33%) inflation. With padding, very short inputs get rounded up to the next multiple of 4, adding a few more bytes.
What's the difference between Base64 and Base64url?
Two characters in the alphabet. Standard Base64 uses '+' and '/' for the last two of the 64 symbols, plus '=' for padding. Base64url replaces '+' with '-' and '/' with '_' so the output is safe to put in URLs and filenames without further escaping. JWTs additionally omit the '=' padding entirely. The two variants are not directly interchangeable; you must transcode at the boundary.
Why does padding (=) sometimes appear and sometimes not?
Padding makes the output length a multiple of 4 — required by some strict decoders (older email gateways, certain TLS certificates). Modern web standards (JWT, OAuth's PKCE, WebPush) drop padding because the length can always be inferred. If you're handing a string to an unknown decoder, include padding; if you're producing JWTs or anything matching RFC 4648 §5, omit it.
Can Base64 handle Unicode strings directly?
No — Base64 encodes bytes, not characters. To Base64-encode a Unicode string you first encode it to bytes (UTF-8 is the universal choice), then Base64 those bytes. Forgetting this step is the source of every 'mojibake' bug in Base64-handling code: encoding the string in one encoding (Latin-1, UTF-16) and decoding under another.
Are data URIs (data:image/png;base64,...) the right way to embed images?
For small icons and inline SVG, yes — they save an HTTP round trip. For anything larger than a few KB, no — the 33% inflation costs more than a separate request, the data can't be cached independently, and browsers parse data URIs on the main thread, blocking rendering.

Sources & references

Authoritative references cited by this piece. Verified by Buğra Sözeri on the dates shown and re-checked at every deploy.

Related

Published May 31, 2026