Base64 encoding converts binary data into ASCII text, but standard Base64 uses characters that have special meaning in URLs. URL-safe Base64 solves this by replacing problematic characters.
Key Takeaways
- 1Standard Base64 uses + and / which are reserved in URLs
- 2URL-safe Base64 replaces + with - and / with _
- 3Padding (=) is often omitted in URL-safe encoding
- 4JWTs use URL-safe Base64 without padding
- 5Always specify which variant you're using in APIs
"This encoding is technically identical to the previous one, except for the 62nd and 63rd alphabet character, as indicated in Table 2."
Standard vs URL-Safe Base64
The difference between standard and URL-safe Base64 comes down to three characters. Standard Base64 uses + and / as the 62nd and 63rd characters, but these have special meaning in URLs—+ represents a space, and / is the path separator. The padding character = conflicts with the query string key=value syntax. URL-safe Base64 solves these conflicts by using safer alternatives.
| Character | Standard Base64 | URL-Safe Base64 | Why Changed |
|---|---|---|---|
| 62nd char | + | - | + means space in URLs |
| 63rd char | / | _ | / is path separator |
| Padding | = | Often omitted | = is reserved in query strings |
Encoding Examples
Most programming languages provide built-in Base64 functions, but you often need to manually handle the URL-safe conversion. The pattern is simple: encode normally, then replace the problematic characters and strip the padding. When decoding, reverse the process by restoring the original characters and adding padding back.
// Standard Base64
const standard = btoa('Hello World!');
// "SGVsbG8gV29ybGQh"
// URL-safe Base64
function toBase64Url(str) {
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
// Decode URL-safe Base64
function fromBase64Url(str) {
str = str.replace(/-/g, '+').replace(/_/g, '/');
// Add padding if needed
while (str.length % 4) str += '=';
return atob(str);
}import base64
data = b"Hello World!"
# Standard Base64
standard = base64.b64encode(data).decode()
# "SGVsbG8gV29ybGQh"
# URL-safe Base64
url_safe = base64.urlsafe_b64encode(data).decode()
# "SGVsbG8gV29ybGQh" (same here, but differs with + or /)
# Without padding
url_safe_no_pad = url_safe.rstrip('=')Both JavaScript and Python examples follow the same pattern. The JavaScript version uses built-in btoa() and atob() functions, while Python offers dedicated urlsafe_b64encode and urlsafe_b64decode methods in the base64 module. Python's approach is cleaner, but you still need to manually strip padding for maximum compatibility.
Base64 in JWTs
JSON Web Tokens are the most common real-world use of URL-safe Base64. Every JWT you've seen—in authentication headers, API responses, or OAuth flows—uses this encoding. Understanding this helps you debug JWTs and implement token validation correctly.
JWTs consist of three parts separated by dots, each encoded with URL-safe Base64 without padding:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
│ │ │
└─ Header (base64url) └─ Payload (base64url) └─ Signature (base64url)You can decode the header and payload parts to inspect a JWT's contents (the signature is binary data). This is useful for debugging authentication issues or understanding what claims a token contains.
When to Use Each
Choosing between standard and URL-safe Base64 depends entirely on where the encoded data will appear. If it touches a URL at any point—query parameters, path segments, or even data that might be shared via URL—use URL-safe encoding. The table below summarizes common scenarios.
| Use Case | Encoding | Reason |
|---|---|---|
| URL query parameters | URL-safe | Avoid breaking URLs |
| JWTs | URL-safe, no padding | Spec requirement |
| Email attachments | Standard | MIME standard |
| Data URLs | Standard | Works within data: scheme |
| File storage | Either | Depends on filename restrictions |