LocalFirstTools.com — data tools that never leave your device
No upload — everything runs on your device

Base64 vs Base64URL: what's the difference?

Last updated

Base64 and Base64URL are the same encoding with two characters swapped: standard Base64 uses + and /, Base64URL replaces them with - and _ and usually drops the = padding. That tiny difference exists because +, / and = all carry meaning inside URLs — and it is the reason a JWT segment sometimes refuses to decode in a standard Base64 tool. The Base64 tool handles standard Base64 locally in your browser.

Both come from RFC 4648

RFC 4648 defines both alphabets: section 4 is “base64” and section 5 is “base64url”, formally “Base 64 Encoding with URL and Filename Safe Alphabet”. Both map every 3 input bytes to 4 output characters from a 64-character set; the first 62 characters (A–Z, a–z, 0–9) are identical, only positions 62 and 63 differ:

                 chars 62,63   padding
Base64             +  /          = required
Base64URL          -  _          = usually omitted

A three-byte worked example

Encode the string Hi? — bytes 72 105 63. Base64 works on 6-bit groups, so the 24 bits regroup from three 8-bit bytes into four 6-bit values:

bytes:   01001000 01101001 00111111        H    i    ?
6-bit:   010010 000110 100100 111111   =  18    6   36   63

alphabet position 63 differs:
Base64    → "SGk/"      (63 = "/")
Base64URL → "SGk_"      (63 = "_")

Same bytes, one different character — and that character (/) is precisely the one a URL path would swallow. Because 3 bytes map to exactly 4 characters, this example needs no padding; padding only appears when the input length is not a multiple of 3.

The padding math

The = signs are pure length bookkeeping: input length mod 3 = 0 → no padding, mod 3 = 2 → one =, mod 3 = 1 → two ==. So "OK" (2 bytes) encodes to T0s= and "A" (1 byte) to QQ==. To restore stripped padding — the everyday chore when handling Base64URL — pad to the next multiple of 4: s + "=".repeat((4 - s.length % 4) % 4). A Base64 string whose length mod 4 equals 1 is impossible; if you see one, characters were lost in transit.

Why the swap matters

  • In a URL, + historically decodes to a space in query strings, / separates path segments, and = separates keys from values. Standard Base64 in a URL either breaks or needs percent-encoding on top — double encoding, twice the bugs.
  • In file names, / is a directory separator on every OS. Base64URL output is filename-safe by construction.

Where you meet each one

Standard Base64: MIME email attachments, data: URLs, Authorization: Basic headers, X.509/PEM certificates, most APIs' binary fields. Base64URL: all three JWT segments (RFC 7519 mandates it — see how to decode a JWT), WebAuthn identifiers, OAuth PKCE code challenges, and short link tokens.

Converting between them

Because only two characters differ, conversion is two string replacements plus padding:

// Base64URL → Base64
let b64 = input.replace(/-/g, "+").replace(/_/g, "/");
b64 += "=".repeat((4 - (b64.length % 4)) % 4);   // restore padding

// Base64 → Base64URL
input.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

The bytes never change — only their textual clothes. If a decode still fails after conversion, the string was truncated or was never Base64 to begin with; the Base64 decode page reports invalid input instead of guessing.

The one thing neither variant is

Encryption. Encoding is a reversible mapping anyone can undo without a key — its 33% size overhead buys transport safety, not secrecy. If a value must be confidential, encrypt it; if it merely must survive a URL, Base64URL is exactly the right tool.

Do it now — locally

Convert text to Base64 and back without uploading. Nothing is uploaded.

Open the Base64

Frequently asked questions

Why does atob() fail on a JWT segment?

JWT segments are Base64URL: they may contain - and _ (which atob rejects in strict engines) and they omit padding. Replace - with +, _ with /, re-add = padding to a multiple of 4, then decode.

Is Base64URL more secure than Base64?

No — neither is security at all. Both are reversible encodings with no key; the difference is purely which characters they use. Anything encoded in either is readable by anyone.

How do I recognize which variant I'm looking at?

A + or / means standard Base64; a - or _ means Base64URL; = padding at the end suggests standard (Base64URL usually drops it). A string with none of those characters is valid in both alphabets — decode it either way.

Why is padding dropped in Base64URL?

The = character has meaning in URLs (key=value) and the padding is redundant — the length mod 4 tells a decoder how many bytes remain. RFC 4648 permits omitting it when the length is otherwise known, which URL contexts exploit.

Why is Base64 output exactly 33% bigger?

Every 6 bits of input become an 8-bit character: 4 output bytes per 3 input bytes, a 4/3 ratio. A 300 KB image becomes a 400 KB data: URL — which is why inlining large assets as Base64 is usually a performance mistake.

What are Base32 and Base58 then?

Sibling encodings from the same family. Base32 (also RFC 4648) uses A–Z and 2–7 — case-insensitive and safe for systems that mangle case, like some DNS labels. Base58, used by Bitcoin addresses, drops visually confusable characters (0, O, I, l). Each trades density for robustness in a different channel.