Base64 vs Base64URL vs Hex: Same Bytes, Three Alphabets

Base64 vs base64url vs hex compared — the +/ vs -_ alphabet swap, padding rules, URL and filename safety, overhead (33% vs 100%), and which to pick.

Published 2026-09-24

Three encodings cover almost every “bytes as text” job, and they differ less than people think. Here’s what actually changes — and a rule for picking one.

Base64 vs base64url: a two-character difference

Same algorithm, same index table, same 3-bytes-to-4-chars math. RFC 4648 §5 changes exactly this:

Index Base64 (§4) Base64URL (§5)
62 + -
63 / _
padding = = (often omitted)

So ++++ in standard base64 is ---- in base64url — same three bytes (FB EF BE), different alphabet. Everything else is identical, which is why the decoder accepts both and just tells you which it found.

Why those two characters specifically

+ and / are the two alphabet characters that already have jobs in other syntaxes:

  • URL query strings: a literal + is decoded as a space by form-urlencoded rules — a+b arrives as a b. / is the path separator, so an encoded token can split into phantom path segments or get normalized away.
  • Filenames: / is illegal on every filesystem (\ too); + is legal but historically flaky in some download stacks.
  • Cookies, HTML attributes, regexes: - and _ need no escaping anywhere; + is a regex quantifier.

The = padding also has URL issues (it’s a query-string separator), which is why base64url deployments — JWTs being the famous one — conventionally drop it. RFC 4648 permits omitting padding when the application spec says so, and JWT’s spec (RFC 7519) says so. If a decoder insists on padding, the fix is mechanical: append = until the length is a multiple of 4.

And hex is a different trade entirely

Hex (base16) uses 0-9a-f: each byte → exactly 2 characters.

Base64 Base64URL Hex (base16)
Alphabet size 64 64 16
Chars per byte 4/3 (1.33×) 4/3 (1.33×) 2 (2.00×)
Padding = optional none needed
URL-safe no (+ /) yes yes
Filename-safe no (/) yes yes
Byte boundaries visible no no yes — 2 chars = 1 byte
Typical home email, data URLs, JSON fields, PEM JWTs, URL tokens, filenames hashes, fingerprints, checksums, colors

For completeness: base32 (A-Z2-7) splits the difference at 1.6× — TOTP secrets are its famous user — and base58 (Bitcoin) drops visually ambiguous characters like 0/O and l/I.

The decision rule

  • Inside JSON, XML, HTML attributes, email bodies → standard base64 with padding. The +// characters are unremarkable there, and strict parsers want the =.
  • In a URL, a filename, a cookie, or a JWT → base64url, padding off.
  • When a human compares or copies the value → hex. Double size, zero ambiguity, and errors in one byte are visible at byte resolution.
  • Never for secrecy: all three are representations, not encryption.

One nuance the table hides: hex is the only format where you can eyeball the bytes. If a decoded signature should start with 30 82 (DER) or a file with FF D8 (JPEG), hex view shows it directly — which is why the tool offers hex output on both directions.

Frequently asked questions

Is base64url a different encoding?

No — it's the same bit-shuffling algorithm (RFC 4648 §5 vs §4). The only change is the last two alphabet characters: - and _ replace + and /. Every index value, the 3→4 byte math, and the = padding rules are identical. Converting between them is literally a two-character search-and-replace.

Why do '+' and '/' cause trouble?

In a URL query string, + is conventionally decoded as a space (the application/x-www-form-urlencoded rule), and / is the path separator — so raw standard base64 can be silently corrupted or change routing. They're also illegal in filenames on every common filesystem. URL-safe characters survive all of those contexts untouched.

Do JWTs use base64 or base64url?

Base64url, with padding omitted. The JWS/JWT specs (RFC 7515/7519) define the three dot-separated segments as base64url encodings with no trailing =. That's why decoding a JWT in a strict standard-base64 decoder can fail — flip the alphabet option (or just paste it into the decoder, which accepts both).

When is hex the better choice?

When humans or diffs read the value: hashes, fingerprints, checksums, color values, key material. Hex is case-insensitive, needs no padding, and each byte maps to exactly two characters so byte boundaries are always visible. It costs double the size — for anything transmitted or stored at volume, base64's 33% wins.

Can I percent-encode standard base64 instead of switching to base64url?

You can — %2B for +, %2F for /, %3D for = — but the payload then swells further and every consumer must decode twice. Base64url exists precisely so you don't have to.