Your token works in Postman and fails in the browser. The decoder says invalid input on a string that looks perfectly fine. Two characters are the difference, and almost nothing tells you which variant you are holding.
Base64URL vs Base64: What Actually Changes
Two characters swap, and the padding usually disappears. That is the entire difference.
| Value | Standard Base64 | Base64URL |
|---|---|---|
| 62 | + | - |
| 63 | / | _ |
| Padding | = | usually omitted |
The other 62 characters are identical. The maths is identical. Decoded output is byte-for-byte the same.
Both variants are defined in RFC 4648, standard Base64 in section 4 and the URL-safe form in section 5.
Why URL Safe Base64 Had to Exist
Because plus and slash both mean something specific inside a URL.
A slash separates path segments. Put one in a Base64 string inside a URL path and you have accidentally created a new directory level.
A plus is worse, because it is ambiguous. In a query string it historically means a space, so a+b may arrive at your server as a b. Your string is now corrupt and nothing errored.
Equals signs are the third problem. They separate keys from values, so they get percent-encoded into %3D — three characters where you had one.
In our experience the plus-becomes-space bug is the nastiest of the three, because it fails silently and only on strings unlucky enough to contain a plus.
JWT Base64URL: Where You Meet It Most
Every JSON Web Token is three base64url segments joined by dots.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Header, payload, signature. Notice the dash and underscore in that third segment — a standard decoder will reject them.
Pasting the whole token into a decoder fails too, because the dots are not valid Base64 characters. Split on the dots first, then decode each part.
Our JWT decoder handles the splitting and the variant automatically, which is faster than doing it by hand.
One caveat that costs people real time: never re-encode a segment and expect the signature to still verify. The signature covers the exact original text, so adding padding back invalidates it.
Base64 Padding Removal and How to Put It Back
Pad the string with equals signs until its length divides by four.
Two lines in most languages:
// JavaScript
function fromUrlSafe(s) {
const swapped = s.replace(/-/g, '+').replace(/_/g, '/');
return swapped + '='.repeat((4 - swapped.length % 4) % 4);
}
# Python has it built in
import base64
base64.urlsafe_b64decode(token + '=' * (-len(token) % 4))
Many libraries tolerate missing padding already, so try decoding before you add it. Node accepts base64url as an encoding name from version 16 and needs no manual work at all.
PHP and browser JavaScript both need the manual swap. Neither has a built-in URL-safe function.
How to Spot Which Variant You Have
Look for the giveaway characters. A dash or underscore means base64url. A plus or slash means standard.
The awkward case is a string containing none of them, which is common for short inputs. Both decoders then produce identical output and it does not matter.
Our Base64 to file decoder detects the variant and converts it before decoding, then tells you it did so. That message alone has saved a few confusing support threads.
When to Reach for Each One
- Anywhere in a URL — path, query string, or fragment — use base64url.
- JWTs and OAuth — base64url, and it is mandatory rather than a preference.
- Filenames — base64url, though case-insensitive filesystems can still collide.
- Email and MIME — standard Base64, with 76-character line wrapping.
- Data URIs — standard Base64, since the browser expects it.
- JSON payloads — either works; standard is the convention.
The general rule we give: if the string will ever appear in a URL, use the URL-safe variant from the start. Converting later is easy but remembering to is not.
Wrapping Up
Base64URL vs Base64 comes down to two characters and some missing equals signs. The algorithm is the same and the decoded bytes are identical.
When a decoder rejects a string that looks valid, check for dashes and underscores first. That one glance solves it more often than anything else.
More background in what Base64 encoding is, and working code for every language in encoding and decoding Base64 in JavaScript, Python and PHP.
