How to convert Base64URL to Base64
- Paste the Base64URL into Input · Base64URL. Padding is optional, and standard
+and/in the input are fine too. - The standard Base64 appears in Output · Base64, with
-and_turned into+and/and the padding restored. - Under the output, Padding (=) is on by default; Line wrap can break the result at 64 characters for PEM or 76 for MIME.
- Copy output. The Base64 → Base64URL toggle, or Base64 to Base64URL, goes the other way.
How much padding to add
Base64 works in groups of 4 characters. Base64URL usually drops the = signs that fill the last group, so the length tells you how many to put back:
| Length mod 4 | Padding to add | Example |
|---|---|---|
| 0 | none | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (36 characters) |
| 2 | == | PDw_Pz8-Pg (10) becomes PDw/Pz8+Pg== |
| 3 | = | eyJyb2xlIjoiYWRtaW4-In0 (23) becomes eyJyb2xlIjoiYWRtaW4+In0= |
| 1 | invalid | a single leftover character cannot hold a whole byte |
The well-known example payload eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ is 70 characters, a remainder of 2, so it becomes the same string with == appended. The padding guide has the full reasoning.
Why you might need standard Base64
Many decoders accept only the standard alphabet, and they fail in different ways when given Base64URL:
- GNU coreutils
base64 -dprints whatever it decoded up to the first-or_, thenbase64: invalid input. - Java
Base64.getDecoder()throwsIllegalArgumentException;Base64.getUrlDecoder()is the one for URL-safe input. - .NET
Convert.FromBase64Stringthrows aFormatExceptionfor-,_or a missing=. - Python
base64.b64decodeis the dangerous one: by default it discards characters outside the standard alphabet without an error.b64decode("PDw_Pz8-Pg==")returnsb'<<\x0f\xcf\xc3\xe0'instead ofb'<<???>>'.
The last case is worth a test in any code that handles tokens. Wrong bytes with no exception usually surface much later, as a failed signature check or corrupted data.
Base64URL to Base64 in code
import base64
def b64url_to_bytes(s: str) -> bytes:
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
b64url_to_bytes("PDw_Pz8-Pg") # b'<<???>>'
base64.b64encode(b64url_to_bytes("PDw_Pz8-Pg")).decode() # 'PDw/Pz8+Pg=='
-len(s) % 4 is the number of missing = signs: 0, 1 or 2 for valid input. urlsafe_b64decode needs the padding, which is why it is added before the call.
function b64urlToB64(s) {
const b = s.replace(/-/g, "+").replace(/_/g, "/");
return b + "=".repeat((4 - (b.length % 4)) % 4);
}
b64urlToB64("PDw_Pz8-Pg"); // 'PDw/Pz8+Pg=='
In Node.js, Buffer.from(s, "base64url").toString("base64") does the same in one call. See the Python, Java and C# pages for the URL-safe decoders in each standard library.
Common problems
- “the last character group is incomplete”. The length leaves a remainder of 1. Part of the string was lost, often at a line break or because a URL was truncated.
- A
.in the input. JWTs join three Base64URL segments with dots. Convert one segment at a time; the JWT decoder takes the whole token. %3Din the string. The value was percent-encoded on top of Base64URL. Decode it with URL decode first.
FAQ
How do I add the padding back to Base64URL?
Pad the end with = to a multiple of 4 characters: two signs for a remainder of 2, one for a remainder of 3. A remainder of 1 cannot happen in valid data. This converter does it for you.
Why does base64 -d fail on a JWT?
GNU base64 -d only knows the standard alphabet and stops with invalid input at the first - or _. Convert the segment here first, or use basenc --base64url -d, which reads the URL-safe alphabet.
What if the string has one character too many?
A Base64URL string whose length leaves a remainder of 1 when divided by 4 is cut off or has an extra character. The converter reports the incomplete last group; check how the value was copied.
Do I need standard Base64 to read a JWT?
Not with this site: the JWT decoder and the Base64URL decoder read Base64URL directly. Convert to standard Base64 when another tool or library only accepts that form.