Base64 vs Base64url
On this page
TL;DR. Standard base64 (
+,/) is the historical default. Base64url (-,_) replaces the two URL-unsafe characters so the output can sit in a URL or filename without escaping. Both encode the same bytes and share one padding rule (RFC 4648 §3.2). Only the surface alphabet differs.
At a glance
| Standard base64 | Base64url (RFC 4648 §5) | |
|---|---|---|
| Position 62 | + | - |
| Position 63 | / | _ |
Padding (=) | Included unless the referring spec says otherwise (RFC 4648 §3.2) | Same rule (§3.2). JWS omits it (RFC 7515) |
| URL-safe out of the box | No | Yes |
| MIME / email | Standard | No |
| JWT | No | Standard |
| Filenames | Risky | Safe |
| Spec | RFC 4648 §4 | RFC 4648 §5 |
What changes
The 64-character alphabet differs in the last two characters:
Standard: ABC...XYZabc...xyz0123456789+/
Base64url: ABC...XYZabc...xyz0123456789-_
That’s it. Positions 62 and 63 swap from URL-reserved characters to URL-safe ones. Everything else (positions 0–61) is identical.
Padding is not part of the difference. RFC 4648 §3.2 sets one padding rule for both alphabets, covered below.
Why two variants exist
When base64 was specified for email (RFC 989, 1987), the alphabet was chosen to maximize compatibility with the 7-bit ASCII-only mail infrastructure of the time. URLs weren’t a consideration.
When the web came along, putting base64 in URL parameters became
common — but + and / are URL-reserved, so the encoded value had to
be URL-encoded again (+ → %2B, / → %2F). That’s wasteful and
error-prone, especially for tokens that pass through multiple systems.
RFC 4648 (2006) standardized base64url to fix this without breaking the existing standard.
Where each is the right choice
Use standard base64 when:
- Email attachments / MIME bodies. Standards explicitly require the standard alphabet.
- Decoding values from systems that emit standard form (most CLI tools, most language stdlib defaults).
- HTTP headers. Most header values aren’t URL-encoded, so
+and/don’t conflict. - Configuration files (YAML, JSON, .env). No URL escaping happens.
- Database BLOB-as-string columns. Same reasoning.
Use base64url when:
- JWT. All three parts of every signed JWT (JWS) are base64url-encoded, and RFC 7515 §2 says to omit the padding. Encrypted JWTs (JWE) have five dot-separated parts (header, encrypted key, IV, ciphertext, tag), and those are base64url too.
- URL query parameters.
?token=<base64url>works directly;?token=<base64>needs escaping. - URL path segments.
/share/<base64url>is fine;/share/<base64>would have/mid-path. - Cookie values. Either works, but base64url avoids any escaping concerns.
- Filenames. Most filesystems accept
-and_but reject/.+is allowed but visually unusual. - OAuth state and PKCE codes. RFC 7636 specifies base64url.
- Any custom token / identifier you’ll embed in URLs. Avoids the URL-encoding step.
Converting between them
A two-line conversion in any language:
// Standard → URL-safe
const urlSafe = standard.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
// URL-safe → Standard
const padded = urlSafe + "=".repeat((4 - urlSafe.length % 4) % 4);
const standard = padded.replace(/-/g, "+").replace(/_/g, "/");
The bytes encoded are identical; only the surface alphabet differs.
Padding: keep it or strip it?
Padding (=) makes the length a multiple of 4. RFC 4648 §3.2
says implementations must include that padding unless the referring
specification says otherwise. §5 only notes that = may be
skipped when the data length is known implicitly. For signed JWTs (JWS),
RFC 7515 is the spec that says to omit the trailing =.
Other systems may insist on it.
- Standard base64 — keep padding. Many decoders reject unpadded input.
- Signed JWT (JWS) — RFC 7515 says to omit the trailing
=. - Base64url otherwise — include
=unless the referring specification says otherwise, or the length is known implicitly. The decoder accepts both.
If you’re consuming someone else’s base64url output and the decoder
fails with “invalid length,” try adding = until the length is a
multiple of 4.
Same input, both outputs
Original bytes: [0xff, 0xfb, 0xff, 0xfb] (4 bytes that exercise + and /)
Standard: "//v/+w=="
Base64url: "__v_-w" (no padding)
Base64url: "__v_-w==" (padding kept)
Notice how the standard output uses both + and /, while the URL-safe
version uses _ and -. Same data, different alphabet.
Languages that distinguish them
Most modern standard libraries provide both:
// Node.js (16+)
Buffer.from(text).toString("base64");
Buffer.from(text).toString("base64url");
// Python
import base64
base64.b64encode(data) // standard
base64.urlsafe_b64encode(data) // URL-safe
// Go
import "encoding/base64"
base64.StdEncoding.EncodeToString(data) // standard
base64.URLEncoding.EncodeToString(data) // URL-safe with padding
base64.RawURLEncoding.EncodeToString(data) // URL-safe without padding
// Rust (base64 crate)
use base64::{Engine, engine::general_purpose};
general_purpose::STANDARD.encode(data)
general_purpose::URL_SAFE_NO_PAD.encode(data)
When the library has both, pick the right one explicitly. Mixing silently leads to the “works in dev, breaks in prod” pattern.
Common mistakes
- Encoding standard, decoding URL-safe (or vice versa) without normalizing. Roundtrips silently fail because the byte values at positions 62 and 63 don’t match.
- URL-encoding base64url.
-and_are unreserved URL characters, so a standards-compliant URL encoder (such asencodeURIComponent) leaves them alone. It does not turn-into%2D. If a downstream tool URL-encodes the string anyway, the only character that gets escaped is the padding=, as%3D. Strip the padding first if that escaping is a problem, or skip the extra URL-encoding step. - Decoding base64url with a strict standard decoder. Fails on
-or_. Normalize first. - Stripping padding when the consumer requires it. Always test both directions of your encode/decode pair before deploying.
Tools on this site
- Standard encoder/decoder — toggle URL-safe with the checkbox
- Base64url focused — URL-safe by default
- What is Base64? — fundamentals if you need them
- Base64 isn’t encryption — encoding vs hashing vs encrypting