โ† Back to Blog

Base64 URL-Safe Encoding Guide

2026-04-08 ยท 5 min read

โ† Back to Blog

Base64 URL-Safe Encoding Guide

ยท 5 min read

Why URL-Safe Base64 Is Needed

Standard Base64's alphabet contains two characters with special URL semantics: the plus sign (+) is interpreted as a space in URL query strings, and the forward slash (/) is the URL path separator. When standard Base64-encoded data appears in a URL, these characters cause the data to be misinterpreted or truncated, requiring percent-encoding (+โ†’%2B, /โ†’%2F) for safe transmission.

Base64URL (defined by RFC 4648) solves this by simply replacing these two characters: hyphen (-) replaces plus (+), and underscore (_) replaces slash (/). The resulting string can be placed directly in URLs or filenames without any additional encoding.

Base64 vs Base64URL Character Comparison

The two encoding methods are identical; the only difference is the substitution of two characters in the alphabet. The padding character equals (=) is often optional in Base64URL โ€” many implementations omit it because the receiver can calculate the required padding from the string length.

Standard Base64:  A-Z a-z 0-9 + / =
Base64URL:        A-Z a-z 0-9 - _ (= optional)

Example:
Standard:  "f+/A" โ†’ URL-encoded โ†’ "f%2B%2FA"
Base64URL: "f-_A" โ†’ safe in URLs as-is

Base64URL in JWT

JWT (JSON Web Token) is one of the most important use cases for Base64URL. A JWT's structure is three Base64URL-encoded strings separated by dots (.): header.payload.signature. The header and payload are both Base64URL encodings of JSON objects, and the signature is a HMAC or RSA signature over the first two parts, also Base64URL-encoded.

Since JWTs are typically passed as HTTP headers (Authorization: Bearer ...) or URL parameters, using Base64URL rather than standard Base64 is crucial. In URL parameters, if a token contains standard Base64's + or /, it may be misinterpreted by HTTP servers, causing authentication failures.

Base64URL Implementations in Various Languages

// JavaScript
function toBase64URL(str) {
  return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function fromBase64URL(s) {
  s = s.replace(/-/g, '+').replace(/_/g, '/');
  s += '=='.slice(0, (4 - s.length % 4) % 4);
  return atob(s);
}

# Python
import base64
def to_base64url(data):
    if isinstance(data, str):
        data = data.encode('utf-8')
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode('ascii')

def from_base64url(s):
    s += '=' * (4 - len(s) % 4)
    return base64.urlsafe_b64decode(s)

// Go
import "encoding/base64"
encoded := base64.RawURLEncoding.EncodeToString(data)
decoded, err := base64.RawURLEncoding.DecodeString(encoded)

Applications in OAuth and PKCE

OAuth 2.0's PKCE (Proof Key for Code Exchange) extension uses Base64URL to encode the SHA-256 hash of the code_verifier, generating the code_challenge. This code_challenge is sent to the authorization server as a URL parameter, so it must be URL-safe encoded.

// PKCE code_challenge ็”Ÿๆˆ็คบไพ‹
// PKCE code_challenge generation example
async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

Using Base64URL in Filenames

Base64URL is also suitable for use in filesystems. Standard Base64's slash (/) is a path separator in most operating systems โ€” if a standard Base64 string is used as a filename, / would be interpreted as a directory separator, causing file creation to fail or files to be created in wrong directories. Base64URL's underscore (_) and hyphen (-) are valid filename characters in all mainstream filesystems.

A common use is generating content-hash-based filenames. Encoding a file's SHA-256 hash with Base64URL produces a compact, URL-safe, filesystem-safe unique identifier suitable as a cache key or filename for content-addressed storage.

Best Practices for Padding Handling

There are two schools of thought on padding in Base64URL: RFC 4648 allows unpadded Base64URL (called "unpadded base64url"), while some older systems require padding to be retained. The most robust approach for decoding is: before decoding, if the string length is not a multiple of 4, add equals signs to fill it.

// ๅฎ‰ๅ…จ็š„ Base64URL ่งฃ็ ๏ผˆๅค„็†ๆœ‰ๆ— ๅกซๅ……ไธค็งๆƒ…ๅ†ต๏ผ‰
// Safe Base64URL decoding (handles both padded and unpadded)
function safeBase64URLDecode(input) {
  // ่กฅๅ…จๅฏ่ƒฝ็ผบๅคฑ็š„ๅกซๅ……
  const padded = input + '=='.slice(0, (4 - input.length % 4) % 4);
  // ่ฝฌๅ›žๆ ‡ๅ‡† Base64
  const standard = padded.replace(/-/g, '+').replace(/_/g, '/');
  return atob(standard);
}

Try the online tool now โ€” no installation, completely free.

Open Tool โ†’

Try the free tool now

Use Free Tool โ†’