Back to Blog
OpsecForge Security TeamWeb DevelopmentSources reviewed 2026-09-08

Base64 vs Base64URL: When URL Safety Matters

A practical guide to understanding Base64 and Base64URL encoding differences, common bug patterns, and secure implementation across JavaScript, Python, Go, and Java.

Primary source: authoritative reference

Compatibility boundary — Use the alphabet and padding rules required by the receiving protocol

If you've ever debugged a JWT that works in one context but fails in another, or wondered why your URL parameters get corrupted after Base64 encoding, you've likely encountered the subtle but critical differences between Base64 and Base64URL encoding. These two encoding schemes are nearly identical—until they're not, and that difference breaks production systems.

Try it locally

Convert Base64 or Base64URL in your browser

Encode or decode a sample without sending its contents to OpsecForge.

Open the Base64 Converter →

The Core Problem: URL-Safety

Standard Base64 can produce +, /, and =. Those characters may require escaping or special handling in URL components. Base64URL substitutes a URL- and filename-safe alphabet while keeping the same 6-bit encoding scheme. Neither encoding encrypts or authenticates the underlying bytes.

Character Comparison

Standard Base64 uses the alphabet A-Z, a-z, 0-9, +, and /, with = as a pad character. RFC 4648 section 5 defines the URL- and filename-safe alphabet by replacing + with - and / with _. Padding may be omitted only when the specification that uses Base64URL permits it.

| Standard Base64 | Base64URL | Decimal Value | |-----------------|-----------|---------------| | + | - (hyphen) | 62 | | / | _ (underscore) | 63 | | = | (omitted) | Padding |

Encoding Process

Both encodings follow identical 6-bit chunking but diverge at character selection and padding.

Example: Encoding "Hello"

Given input bytes [0x48, 0x65, 0x6c, 0x6c, 0x6f] ("Hello"), standard Base64 produces SGVsbG8=. An unpadded Base64URL representation is SGVsbG8. The alphabets do not diverge for this input, but the padding convention can.

When They Diverge

Consider the ASCII input 00? (bytes [0x30, 0x30, 0x3f]).

  • Standard Base64: MDA/
  • Base64URL: MDA_

The + and / in standard Base64 become - and _ in Base64URL, making it safe for URLs.

Padding Differences

Standard Base64 Padding Rules

Base64 encoding produces 4 output characters for every 3 input bytes. When input isn't divisible by 3, padding ensures alignment. If the input length is 3n + 1 bytes, two = characters are added. If it's 3n + 2 bytes, one = character is added.

Base64URL Padding

Base64URL applications often omit padding, but RFC 4648 requires padding unless the referring specification explicitly says otherwise. Follow the protocol you are implementing; JWT compact serialization is one common unpadded use.

When to Use Each Encoding

Use Standard Base64 When:

  • Embedding in HTML/CSS (except URLs)
  • Email attachments (MIME)
  • Binary-to-text in configuration files
  • Data URIs (with proper escaping)
  • Protocols that expect RFC 2045 compliance

Use Base64URL When:

  • JWT header and payload segments
  • URL query parameters
  • URL path segments
  • File names in URLs
  • OAuth state parameters
  • Protocol fields that explicitly require the RFC 4648 URL-safe alphabet

JWT: The Primary Use Case

JWT compact serialization uses Base64URL-encoded segments without padding, as specified by RFC 7519 and its underlying JOSE rules. Use a maintained JWT library for parsing and signature verification; Base64URL decoding alone does not validate a token.

JWT structure:

base64url(header).base64url(payload).base64url(signature)

A synthetic JWT-shaped example:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.Signature

Notice the absence of +, /, and = in the encoded segments.

Common Bug Patterns

Bug 1: Using Standard Base64 for URL Parameters

Using standard Base64 in URLs is problematic because characters like + and / are interpreted differently by URL parsers, breaking the data.

BROKEN:

// Standard Base64 in URL parameter can corrupt data
const data = btoa('00?'); // Produces 'MDA/'
window.location.href = `/search?data=${data}`;

The / requires the URL-component handling expected by the receiving application.

CORRECT:

// Using Base64URL for URL safety
function base64UrlEncode(str) {
  const base64 = btoa(unescape(encodeURIComponent(str)));
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
const urlSafeBase64 = base64UrlEncode("00?");
window.location.href = `/search?data=${urlSafeBase64}`; // ?data=MDA_

Bug 2: Expecting Padding in Base64URL Decoding

JWT libraries or custom decoders must be prepared for the absence of padding in Base64URL.

BROKEN (Python):

import base64

# JWT segment (no padding)
jwt_payload = "eyJ1c2VyIjoiYWRtaW4ifQ"
# This fails: binascii.Error: Incorrect padding
base64.b64decode(jwt_payload)

CORRECT (Python):

import base64

def base64url_decode(input_str):
    # Add padding if necessary
    padding_needed = 4 - len(input_str) % 4
    if padding_needed != 4:
        input_str += '=' * padding_needed
    return base64.urlsafe_b64decode(input_str)

decoded = base64url_decode("eyJ1c2VyIjoiYWRtaW4ifQ")

Bug 3: Mixing Encodings in Cryptographic Operations

Using standard Base64 for one part of a JWT (like payload) and Base64URL for another (like signature) without proper handling can lead to signature verification failures or vulnerabilities. Always use Base64URL consistently for JWT segments.

Language-Specific Implementations

JavaScript / TypeScript

// Modern Node.js (v14.18+) has direct support
const encoded = Buffer.from(data).toString('base64url');
const decoded = Buffer.from(encoded, 'base64url').toString();

// Browser + Node compatible function
function base64UrlEncode(str) {
  const base64 = btoa(unescape(encodeURIComponent(str)));
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

Python

import base64

# Encoding (no padding)
encoded = base64.urlsafe_b64encode(b'hello world').rstrip(b'=').decode('ascii')

# Decoding (handles missing padding)
def base64url_decode(input_str):
    padding_needed = 4 - len(input_str) % 4
    if padding_needed != 4:
        input_str += '=' * padding_needed
    return base64.urlsafe_b64decode(input_str)

Go

import (
    "encoding/base64"
)

// Encoding (no padding)
encoded := base64.RawURLEncoding.EncodeToString(data)

// Decoding (no padding expected)
decoded, err := base64.RawURLEncoding.DecodeString(encoded)

Security Implications

Encoding is not a security control

Choosing the wrong alphabet can cause parsing or interoperability failures. Choosing the right alphabet does not make untrusted data safe, prevent injection, or verify a JWT signature. Validate the decoded data for its destination and use the protocol's normal authentication and authorization controls.

Quick Reference Table

| Aspect | Standard Base64 | Base64URL | | :-------------- | :-------------- | :------------- | | Character 62 | + | - (hyphen) | | Character 63 | / | _ (underscore) | | Padding (=) | Required unless specified otherwise | Protocol-dependent | | Use in URLs | May require escaping | URL-safe alphabet | | JWT Support | No | Yes (required) | | RFC Reference | RFC 4648 §4 | RFC 4648 §5 |

Local Base64 text conversion

Encode and decode sample UTF-8 text with standard or URL-safe Base64 in the loaded browser page. Do not treat encoded output as encrypted or safe to disclose.

Open Base64 Converter →

Conclusion

Understanding the difference between Base64 and Base64URL helps prevent avoidable parsing and interoperability bugs in JWTs, URL parameters, and web APIs.

Remember:

  • Use Base64 for internal storage and email.
  • Use Base64URL when the receiving protocol requires its URL-safe alphabet.
  • Always handle padding correctly when implementing custom decoders.
  • Use your language's built-in Base64URL functions when available.

📬 Stay Ahead of Threats Continue with the JWT Decoder for JWT segments, the URL Encoder for query components, or browse the complete tools center.

Primary references

Share this: