Base64 vs Base64URL: what's the difference and when to use which
base64 encoding developer-tools
Base64 vs Base64URL: what's the difference and when to use which
If you have ever encoded a binary blob into a string, you have used Base64. If you have ever put that string into a URL, you have probably hit a bug and discovered Base64URL. The two are siblings — same algorithm, different alphabet — but the difference matters a lot in practice.
This guide covers the character-level differences, why + and / break URLs, how padding interacts with both, and a decision tree for picking the right variant.
Want to encode or decode either variant in your browser? Use the free Base64 Encoder/Decoder — handles UTF-8, files, and URL-safe mode.
What Base64 actually is
Base64 encodes any byte sequence into a string drawn from a 64-character alphabet. Every 3 input bytes become 4 output characters. The alphabet is defined in RFC 4648:
Standard Base64 alphabet:
ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 + /Base64URL alphabet:
ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 - _
The only difference is two characters:
| Position | Standard | URL-safe |
| 62 | + | - |
| 63 | / | _ |
That is it. The algorithm is identical. The encoding and decoding logic is identical. Only those two characters change.
Why the two characters matter
The standard alphabet uses + and /. Both have special meanings in URLs:
+in a query string is decoded as a space by most server frameworks./is the path separator — embedding it in a query parameter or path segment breaks routing.- Neither is safe to put in a URL without percent-encoding, which defeats the purpose of using Base64 in the first place.
- and _, both of which are URL-safe and need no escaping.Example
Encode the bytes of Hello, World! in standard Base64:
SGVsbG8sIFdvcmxkIQ==
Now imagine putting that in a URL:
https://example.com/redirect?token=SGVsbG8sIFdvcmxkIQ==
The == padding is technically fine in a query string, but if the token contained + or /, the URL would break. For example, encoding the bytes ~??? gives fj8/Pw== in standard Base64 — the / characters would be misinterpreted as path separators.
In Base64URL, the same bytes give fj8_Pw==, which is safe to embed anywhere in a URL.
Padding: = vs no padding
Both variants use = for padding to make the output length a multiple of 4. Some URL-safe contexts strip the padding:
- JWTs (JSON Web Tokens) strip
=padding entirely. - Many URL parameter schemes strip it because
=is the key-value separator in query strings.
= until the length is a multiple of 4.function restorePadding(str) {
return str + '='.repeat((4 - (str.length % 4)) % 4);
}
The RuMystic Base64 tool handles this automatically when you toggle URL-safe mode.
The decision tree
Use this flowchart to pick the right variant:
+ and / have special meaning?btoa() and Buffer.toString('base64') produce.Language-by-language reference
JavaScript (browser)
// Standard Base64
const encoded = btoa('Hello, World!'); // SGVsbG8sIFdvcmxkIQ==
const decoded = atob(encoded);// Base64URL (manual)
const urlSafe = btoa('Hello, World!')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
Note: btoa and atob only handle Latin-1 characters. For UTF-8 strings, encode first:
const utf8ToBase64 = (str) => btoa(unescape(encodeURIComponent(str)));
const base64ToUtf8 = (b64) => decodeURIComponent(escape(atob(b64)));
Or use TextEncoder + manual base64 conversion for full Unicode support.
Node.js
// Standard Base64
const encoded = Buffer.from('Hello, World!').toString('base64');// Base64URL (Node.js 16+)
const urlSafe = Buffer.from('Hello, World!').toString('base64url');
The base64url encoding was added in Node.js 16 and handles the alphabet swap and padding removal automatically.
Python
import base64Standard Base64
encoded = base64.b64encode(b'Hello, World!').decode() # SGVsbG8sIFdvcmxkIQ==Base64URL
url_safe = base64.urlsafe_b64encode(b'Hello, World!').decode() # SGVsbG8sIFdvcmxkIQ==
Note that for this specific input, both produce the same output because there are no + or / characters. The difference only shows up when the encoded bytes happen to map to positions 62 or 63.
Go
import "encoding/base64"encoded := base64.StdEncoding.EncodeToString([]byte("Hello, World!"))
urlSafe := base64.URLEncoding.EncodeToString([]byte("Hello, World!"))
urlSafeNoPad := base64.RawURLEncoding.EncodeToString([]byte("Hello, World!"))
Go has four encodings built in: StdEncoding, URLEncoding, RawStdEncoding (no padding), RawURLEncoding (no padding). Pick the one that matches your use case.
Common pitfalls
Pitfall 1: Mixing the alphabets
If you encode with standard Base64 and decode with Base64URL (or vice versa), the result is wrong whenever the input contains +, /, -, or _.
Fix: always know which variant produced the string. If unsure, try both:
function flexibleDecode(b64) {
try {
return atob(b64);
} catch {
return atob(b64.replace(/-/g, '+').replace(/_/g, '/'));
}
}
Pitfall 2: Forgetting UTF-8
btoa('café') throws InvalidCharacterError in browsers because é is not Latin-1. Always encode UTF-8 first:
btoa(new TextEncoder().encode('café').reduce((s, b) => s + String.fromCharCode(b), ''));
Pitfall 3: Truncation
Base64 is not a hash — it is fully reversible. But if you truncate the encoded string, you cannot decode it back. This sounds obvious, but it bites people who try to "shorten" a Base64 token by chopping the end.
Pitfall 4: Comparing encoded strings for equality
If you compare two Base64 strings for equality, make sure both use the same variant AND the same padding. SGVsbG8= and SGVsbG8 are the same bytes but different strings.
Fix: decode both and compare the raw bytes, or normalize both to the same variant and padding before comparing.
When NOT to use Base64 at all
Base64 is a transport encoding, not a compression scheme. It makes data 33% larger. Do not use it when:
- You could send the binary directly (modern HTTP handles binary fine).
- You are trying to "obfuscate" data — Base64 is trivially reversible.
- You are storing data in a database — store the raw bytes.
- You must put binary data into a text-only context (JSON, XML, email, URL).
- You need a string representation that survives copy-paste and transport intact.
Try it yourself
Open the free RuMystic Base64 Encoder/Decoder:
- Toggle between standard and URL-safe modes.
- Handles UTF-8 correctly (no
btoaLatin-1 limitation). - Drag in a file to encode or decode — file content never leaves your browser.
- No signup, no rate limit, no upload to a server.