Base64URL Explained: Why JWTs Drop the +, / and = Characters
If you've ever pasted a JWT into a decoder and noticed that it looks almost like Base64 — but not quite — you've met Base64URL. It's the same idea as regular Base64, with three small alphabet changes that make the string
If you've ever pasted a JWT into a decoder and noticed that it looks almost like Base64 — but not quite — you've met Base64URL. It's the same idea as regular Base64, with three small alphabet changes that make the string safe to put in a URL or an HTTP header without escaping.
I built base64decodetool.com after spending too many late nights pasting tokens into random sites and wondering whether the tool was encoding or decoding the right variant. This walkthrough is the mental model I wish I'd had on day one. Disclosure: Nagesh built that toolkit; everything below is the same math it uses.
What Base64 actually does
Binary data is a stream of bits. Email, JSON, and many HTTP headers prefer printable text. Base64 groups every 3 bytes (24 bits) into four 6-bit numbers, then maps each 6-bit value to one of 64 characters:
A–Z, a–z, 0–9, +, /
If the input length isn't a multiple of 3, Base64 pads the end with = so the output length stays a multiple of 4.
Example (classic Base64):
- Input text:
Man - Bytes:
77 97 110 - Base64:
TWFu
That's it. Encoding, not encryption. Anyone can reverse it instantly. If you need secrecy, use AES (or similar). If you need a password store, use a slow hash like bcrypt or argon2 — not Base64.
Why URLs hate + and /
A JWT is usually sent in a query string or an Authorization: Bearer … header. In a URL:
-
+is often treated as a space -
/starts a path segment -
=can be treated as a query-parameter separator
So if a token contained those characters, intermediaries would mangle it. The fix in RFC 4648 section 5 is Base64URL:
- Standard
+becomes- - Standard
/becomes_ -
=padding is usually omitted
Same 6-bit math. Different alphabet. Often no padding.
Worked example: a tiny JWT header
A JWT has three Base64URL parts separated by dots: header.payload.signature.
A common header JSON is:
{"alg":"HS256","typ":"JWT"}
Encoded with standard Base64 you might see something with +///= depending on the bytes. Encoded with Base64URL (what real JWTs use) the first segment almost always starts with eyJ — because { is e, " is y, and so on in Base64.
Try it by hand for the single character { (ASCII 123):
- 123 in binary is
01111011 - Pad to a full Base64 group as your library requires
- The first output character for a JSON object header lands on
e, which is why so many tokens open witheyJ
Decode only the header and payload. The signature is a cryptographic check, not something a decoder "unlocks." If a tool claims to "verify" a token without your secret or public key, be suspicious.
Padding tricks that trip people up
Standard Base64 always pads so the length is a multiple of 4. Base64URL in JWTs usually drops the = characters to keep tokens shorter.
When you decode a bare segment yourself:
- Length mod 4 == 2 → add
== - Length mod 4 == 3 → add
= - Length mod 4 == 0 → nothing to add
- Length mod 4 == 1 → the string is invalid
Many JWT bugs are just missing padding on the library call.
When to use which
Use standard Base64 for:
- email attachments (MIME)
- data URIs like
data:image/png;base64,… - many databases' binary columns exposed as text
Use Base64URL for:
- JWTs and JWS/JWE compact serialization
- OAuth tokens in redirect URLs
- anything that must survive a query string unchanged
Mixing them up is a common bug. If a decoder shows gibberish, try the other alphabet before you assume the data is corrupted.
A quick checklist for debugging
- Count the dots. A JWT should have exactly two.
- Try Base64URL decode on the first two segments (add
=padding if your library requires it). - Confirm the header's
alg. Rejectnoneand unexpected algorithms in production. - Never put secrets in the payload. It is only encoded.
- Verify the signature with your real key — decoding is not verifying.
Try it yourself
Paste a sample token into the JWT view at base64decodetool.com/jwt/, or use the plain Base64 / Base64URL toggles on the main decoder. Everything runs in your browser; nothing is uploaded.
If this cleared up the + vs - confusion, share it with whoever keeps pasting tokens into the wrong alphabet. And if you want a feature added — hex side-by-side, a curl snippet, a particular language example — tell me.
— Nagesh
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.