Base64 vs Base64URL Encoding: What's the Difference?
Base64 and Base64URL are two variants of the same encoding scheme, but they differ in two critical characters. This difference determines whether your encoded data can safely appear in URLs, filenames, and HTTP headers without breaking. If you work with API keys, JWTs, or any token that travels through URLs, understanding this distinction is essential.
The Core Difference
Both Base64 and Base64URL encode binary data into printable ASCII characters using a 64-character alphabet. They share 62 of those characters (A-Z, a-z, 0-9). The difference is the final two characters and the padding:
| Feature | Base64 (RFC 4648 Sec. 4) | Base64URL (RFC 4648 Sec. 5) |
|---|---|---|
| Character 62 | + | - (hyphen) |
| Character 63 | / | _ (underscore) |
| Padding | = (required) | Omitted (usually) |
That is the entire difference. Base64URL replaces + with -, replaces / with _, and drops the trailing = padding characters.
Why Base64 Breaks in URLs
The three characters that Base64URL changes (+, /, =) all have special meaning in URLs:
+is interpreted as a space in query strings (application/x-www-form-urlencoded)/is a path separator=separates keys from values in query parameters
If you include a standard Base64 string in a URL without percent-encoding it first, the URL parser will misinterpret these characters. Consider this example:
// Standard Base64 key
const key = "Abc+Def/Ghi=";
// Placed in a URL:
https://api.example.com/verify?key=Abc+Def/Ghi=
// The server receives:
// key = "Abc Def" (+ became space, / split the path, = confused the parser)
You could percent-encode the Base64 string (Abc%2BDef%2FGhi%3D), but that defeats the purpose of using a compact encoding. Base64URL solves this by using URL-safe characters from the start.
// Base64URL key (same data)
const key = "Abc-Def_Ghi";
// Placed in a URL:
https://api.example.com/verify?key=Abc-Def_Ghi
// The server receives exactly: "Abc-Def_Ghi"
How to Convert Between Them
Base64 to Base64URL (JavaScript)
function base64ToBase64URL(base64) {
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
// Example
base64ToBase64URL("SGVsbG8gV29ybGQ=");
// Returns: "SGVsbG8gV29ybGQ"
Base64URL to Base64 (JavaScript)
function base64URLToBase64(base64url) {
let base64 = base64url
.replace(/-/g, '+')
.replace(/_/g, '/');
// Restore padding
const pad = base64.length % 4;
if (pad === 2) base64 += '==';
else if (pad === 3) base64 += '=';
return base64;
}
// Example
base64URLToBase64("SGVsbG8gV29ybGQ");
// Returns: "SGVsbG8gV29ybGQ="
Where Each Encoding Is Used
Base64 (standard) is used in:
- Email attachments (MIME encoding)
- Data URIs in HTML/CSS (
data:image/png;base64,...) - PEM-formatted certificates and keys
- HTTP Basic Authentication headers
- XML and SOAP payloads
Base64URL is used in:
- JSON Web Tokens (JWT) — all three parts (header, payload, signature) use Base64URL
- API keys that appear in URL query parameters
- OAuth 2.0 code_verifier and code_challenge (PKCE)
- WebAuthn credential IDs
- Content-addressable storage systems
- Any token that might pass through a URL
Rule of thumb: if your encoded data will ever appear in a URL, filename, or cookie, use Base64URL. If it stays within structured formats like email or certificates, standard Base64 is fine.
JWTs Use Base64URL Exclusively
The JWT specification (RFC 7519) mandates Base64URL encoding without padding. This is not optional. If you implement JWT encoding or decoding manually, you must use the URL-safe alphabet.
// A JWT has three Base64URL-encoded parts separated by dots:
// header.payload.signature
const jwt = [
btoa(JSON.stringify({alg:"HS256",typ:"JWT"}))
.replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,''),
btoa(JSON.stringify({sub:"1234567890",name:"John"}))
.replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,''),
"signature_here"
].join('.');
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ.signature_here
Generating Base64URL Keys
Here is how to generate a cryptographically secure Base64URL-encoded API key in several languages:
JavaScript (Browser & Node.js)
function generateBase64URLKey(bytes = 32) {
const buf = new Uint8Array(bytes);
crypto.getRandomValues(buf);
return btoa(String.fromCharCode(...buf))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
// 32 bytes = 43 characters = 256 bits entropy
Python
import secrets
import base64
def generate_base64url_key(byte_length=32):
return base64.urlsafe_b64encode(
secrets.token_bytes(byte_length)
).rstrip(b'=').decode('ascii')
Go
import (
"crypto/rand"
"encoding/base64"
)
func generateBase64URLKey(byteLength int) string {
b := make([]byte, byteLength)
rand.Read(b)
return base64.RawURLEncoding.EncodeToString(b)
}
Note that Go's base64.RawURLEncoding uses Base64URL without padding by default. Python's urlsafe_b64encode includes padding, so you need to strip it manually.
Padding: To Include or Not?
Base64 padding (=) exists to signal that the encoded length is not a multiple of 4. It is needed for correct decoding in contexts where multiple Base64 strings are concatenated. In practice, most modern implementations can decode without padding by inferring the correct length.
The consensus in the API and token world is to omit padding. JWTs omit it. Most API key implementations omit it. Padding characters cause issues in URLs and add no value when keys are transmitted individually.
Summary
| Question | Answer |
|---|---|
| Building an API key? | Use Base64URL (no padding) |
| Working with JWTs? | Use Base64URL (required by spec) |
| Encoding data for email/MIME? | Use standard Base64 |
| Encoding data for data: URIs? | Use standard Base64 |
| Need URL-safe encoding? | Use Base64URL |
| Need to concatenate encoded strings? | Use Base64 with padding |
Generate Base64URL-encoded keys instantly with our free API key generator. Select "Base64URL" format and choose your preferred length.
Recommended Resources
For a deep dive into encoding, JWTs, and the Web Crypto API, JavaScript: The Definitive Guide covers Base64 handling and typed arrays thoroughly. For the cryptographic context of why encoding choices matter, Real-World Cryptography is the go-to reference.