Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

Base64 vs Base64url

On this page
  1. At a glance
  2. What changes
  3. Why two variants exist
  4. Where each is the right choice
    1. Use standard base64 when:
    2. Use base64url when:
  5. Converting between them
  6. Padding: keep it or strip it?
  7. Same input, both outputs
  8. Languages that distinguish them
  9. Common mistakes
  10. Tools on this site

TL;DR. Standard base64 (+, /, padding required) is the historical default. Base64url (-, _, padding optional) replaces the two URL-unsafe characters so the output can sit in a URL or filename without escaping. Both encode the same bytes — only the surface alphabet differs.

At a glance

Standard base64Base64url (RFC 4648 §5)
Position 62+-
Position 63/_
PaddingRequired (=)Optional
URL-safe out of the boxNoYes
MIME / emailStandardNo
JWTNoStandard
FilenamesRiskySafe
SpecRFC 4648 §4RFC 4648 §5

What changes

The 64-character alphabet has three differences:

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.

The padding rule changes too: standard base64 requires = to fill the output to a multiple of 4 characters; base64url makes it optional.

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:

Use base64url when:

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?

The classic gotcha. Three rules:

  1. Standard base64 — keep padding. Many decoders reject unpadded input.
  2. Base64url for JWT — strip padding. The spec requires it.
  3. Base64url for everything else — your choice; 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

Tools on this site