Base64 URL-Safe Encoding Guide
โ 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 โ