DevRivet

Blog ·

Base64 vs Base64URL: Differences and When to Use Each

Base64URL swaps + and / for - and _ and often drops = padding. Learn the differences, when each one fits, and how to convert between them.

Base64 Encoder / Decoder

Encode and decode Base64 instantly in your browser.

Try it free →

Standard Base64 uses the characters + and / and pads output with =. Base64URL replaces + with - and / with _, and it is often written without = padding. Use standard Base64 for email attachments, data URIs and many APIs. Use Base64URL when the encoded value goes into a URL, a filename or a JWT.

You can test either variant with the Base64 Encoder / Decoder. Paste a value, encode or decode it, and compare the output. The rest of this post covers the exact differences, the conversion code, and the mistakes developers hit most often.

The alphabets side by side

Both variants are defined in RFC 4648. The first 62 characters are identical: A–Z, a–z and 0–9. They differ only in the last two symbols and in padding.

  • Standard Base64 (RFC 4648, section 4): index 62 is +, index 63 is /, and = is used for padding.
  • Base64URL (RFC 4648, section 5): index 62 is -, index 63 is _, and padding is optional. Many implementations omit it.

Each Base64 character carries 6 bits. The + and / characters are special in URLs: + can be read as a space in a query string, and / is a path separator. The URL-safe variant avoids both problems without needing percent-encoding.

Examples with the same bytes

Hello encodes the same way in both variants because it contains none of the affected characters:

  • Input bytes: Hello (48 65 6c 6c 6f)
  • Standard Base64: SGVsbG8=
  • Base64URL without padding: SGVsbG8

Bytes that produce index 62 or 63 show the difference. The two bytes 0xFB 0xFF give these outputs:

  • Standard Base64: +/8=
  • Base64URL without padding: -_8

A single byte 0xFF gives /w== in standard Base64 and _w in Base64URL. The padding is dropped in the URL-safe version.

Quick comparison

Use this table when you need to pick a variant or check which one a value uses.

  • Characters 62 and 63: standard uses + and /; URL-safe uses - and _.
  • Padding: standard requires = to make the length a multiple of 4; URL-safe usually omits it.
  • Safe in a URL path or query without encoding: standard is not fully safe (+, / and = need encoding); URL-safe is safe.
  • Typical uses: standard for MIME email, PEM certificates, data: URIs and HTTP Basic auth headers; URL-safe for JWT segments, URL tokens, and IDs or filenames that appear in links.

When to use standard Base64

Use standard Base64 when the receiving system expects it. MIME, PEM and the Basic auth header in HTTP all use the standard alphabet with padding. Changing the alphabet breaks those consumers, even if the value looks fine to you.

Data URIs such as data:image/png;base64,... are a common exception to watch. The payload is standard Base64. If it contains + or /, and you place it in a query string, percent-encode it first with a tool such as the URL Encoder / Decoder.

When to use Base64URL

Use Base64URL when the value must survive a URL, a cookie-like token, or a filename. JSON Web Tokens are the most common case. A JWT is three dot-separated parts, and each part is Base64URL encoded without padding. The dot is the separator, so the encoding must avoid dots. Base64URL does not introduce any dots, because it does not use . at all.

To see how this works in practice, paste a token into the JWT Decoder. The header and payload are Base64URL segments, and the decoder shows them as readable JSON. Decoding a token does not check its signature, so a readable payload is not proof that the token is valid. For the difference between decoding a token and verifying its signature, see Decode JWT vs Verify: What's the Difference?.

Converting between the two in JavaScript

Node.js and modern browsers handle these conversions differently. In Node.js, Buffer supports a base64url encoding name directly. In the browser, btoa and atob only work with the standard alphabet, so you need to convert the characters yourself.

const bytes = Buffer.from([0xfb, 0xff]);
console.log(bytes.toString('base64'));    // +/8=
console.log(bytes.toString('base64url')); // -_8

const std = '+/8=';
const url = std.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
console.log(url); // -_8

const back = url.replace(/-/g, '+').replace(/_/g, '/');
const padded = back + '='.repeat((4 - (back.length % 4)) % 4);
console.log(padded); // +/8=

The padding step works when the length mod 4 is 0, 2 or 3. A length mod 4 of 1 is not valid Base64 in either variant. Such input is corrupt, usually because it was truncated or altered in transit, and adding = characters cannot repair it.

In the browser, atob('-_8=') throws an InvalidCharacterError, because - and _ are not in the standard alphabet. Convert the characters first, then call atob.

Converting between the two in Python

Python's base64 module has separate functions for each alphabet. The URL-safe encoder always pads, so strip the padding yourself if your format requires it.

import base64

data = b'\xfb\xff'
print(base64.b64encode(data))         # b'+/8='
print(base64.urlsafe_b64encode(data)) # b'-_8='
print(base64.urlsafe_b64encode(data).rstrip(b'='))  # b'-_8'

# Decoding unpadded input: add the padding back first
s = b'-_8'
s += b'=' * (-len(s) % 4)
print(base64.urlsafe_b64decode(s))    # b'\xfb\xff'

Common pitfalls

  • Mixing alphabets: a value with - or _ fed to a standard decoder fails. A value with + or / fed to a URL-safe decoder can decode to the wrong bytes or fail. Check which alphabet the source uses.
  • Padding mismatch: some decoders require padding and others do not. If decoding fails on a length that is not a multiple of 4, add = characters before decoding. A length with mod 4 equal to 1 is different: no amount of padding makes it valid, so the input is corrupt and needs to be fetched or generated again.
  • Unencoded + in query strings: a + in a URL is often decoded as a space. Use Base64URL, or percent-encode the value.
  • Assuming Base64 is encryption: both variants are reversible encodings. Anyone can decode them, so do not treat them as secrets. The size overhead is also about 33 percent. See Base64 Size Increase: Why Base64 Makes Data About 33% Bigger for the math.
  • Pasting live secrets into online tools: a JWT or any token can contain live credentials. Before pasting a production value into any online tool, check how that tool handles your input, or use a local script instead. The DevRivet Base64 and JWT tools run in your browser, but you should still confirm that for any tool you use with secrets.

Which one should you choose?

If the specification or library you are calling names the alphabet, use that one. If you are designing a new token or identifier that will appear in a URL, choose Base64URL and omit padding. If you are working with email, certificates, data URIs or HTTP headers, use standard Base64 with padding.

FAQ

Is Base64URL the same as Base64 with the characters swapped?

Mostly, yes. The swap of + to - and / to _ is the core change. Padding is the other difference: Base64URL is often written without trailing = characters, but padding is optional in many decoders.

Why does my JWT not decode with a standard Base64 decoder?

JWT segments use Base64URL without padding. A standard decoder rejects - and _, and it may need padding added back. Convert the alphabet and restore the padding, or use a JWT-aware decoder.

Do I need to add padding before decoding Base64URL?

It depends on the decoder. Some libraries accept unpadded input and others do not. If you control the code, add = characters until the length is a multiple of 4 before calling a decoder that requires padding. If the length mod 4 is 1, the value is corrupt and padding will not help.

Is Base64URL more secure than Base64?

No. Both are encodings, not encryption or hashing. Their only practical difference is which characters appear in the output. Security comes from the signature, encryption, or access control around the value.

Base64 Encoder / Decoder

Encode and decode Base64 instantly in your browser.

Try it free →