URL-Safe Base64 vs Standard Base64
A URL sees standard Base64 before your code does, and that can change the value.
Standard Base64 uses + and / in its alphabet. Both characters have special meaning in URLs , + encodes a space in query strings, and / separates path segments.1 Embedding standard Base64 in a URL without percent-encoding breaks routing and parsing.
URL-safe Base64 solves this by substituting + with - and / with _.2 The resulting strings are safe in any URL component , path, query string, fragment, or HTTP header , without any escaping. All other 62 characters are identical to standard Base64.
How the two variants differ
The only difference between standard and URL-safe Base64 is two characters in the 64-character alphabet: position 62 changes from + to -, and position 63 changes from / to _.2 Everything else about the encoding is identical, including the block alignment, the padding rules, and the 33% size expansion that applies to both variants equally.
Matching alphabet to transport
The encoding length, 33% expansion, and 3-bytes-to-4-characters ratio are identical between both variants. Consequently, converting between variants requires only a text substitution: replace + with -, / with _, and strip or add = padding as needed. No re-encoding of the underlying bytes is necessary. Building on this, both variants produce output of the same length for the same input, and decoders for one variant silently misdecode the other, so always match the variant at both ends. The two substituted characters were chosen because - and _ are unreserved URL characters that survive transmission through every part of a URL, including path segments, query parameters, and fragment identifiers, without requiring any percent-encoding step.
Common pitfalls when mixing variants
Passing standard Base64 to a URL-safe decoder (or vice versa) produces corrupted output without any error in most languages. Python's base64.b64decode() accepts standard Base64; base64.urlsafe_b64decode() accepts URL-safe. Passing the wrong string to either function produces garbage bytes silently. Furthermore, JWT libraries almost always use URL-safe Base64 , passing a standard-Base64-encoded JWT to a JWT library fails signature verification because the decoded header and payload differ from what was signed.3 Padding also differs between contexts: URL-safe Base64 typically strips = while standard Base64 preserves it. The silent corruption is what makes this bug so dangerous: the decoded output is valid bytes, but it is the wrong bytes, and the failure may not surface until a downstream cryptographic verification or JSON parse fails with an error message that does not point back to the encoding mismatch.
Security and best practice
Use URL-safe Base64 for any value that appears in a URL, HTTP header, cookie, or filename where special characters would cause parsing issues. Use standard Base64 for MIME email, PEM files, and data URIs where + and / are safe within the data: scheme. Choosing the wrong variant for the transport context produces values that look correct in logs but fail at the receiving parser, so the choice belongs in the API specification rather than being left to individual implementation decisions.
Making the variant an API contract
Document which variant each field in your API uses, because the choice is an API contract that clients must match exactly. Avoid auto-detecting the variant from the content because + and - can both appear in valid data; ambiguity causes silent decoding failures. Furthermore, URL-safe Base64 without padding is the convention in most modern protocols (JWT, PKCE, WebAuthn); explicitly strip = before encoding and add it back before decoding if your library requires it. A practical way to enforce the contract is to add a validation regex at the API boundary: allow only [A-Za-z0-9_-] for URL-safe fields and only [A-Za-z0-9+/] for standard fields, which catches encoding mismatches before the value reaches any decoder.
URL-safe Base64 in OAuth PKCE and WebAuthn
In OAuth PKCE flows, the code verifier is a cryptographically random string and the code challenge is the SHA-256 hash of the verifier, both encoded as URL-safe Base64 without padding.4 Both values travel in URL query parameters, which makes the URL-safe variant mandatory. Standard Base64 with + or / characters requires percent-encoding in a query string, and many OAuth client libraries parse the URL without first percent-decoding, causing the parameters to arrive at the server with %2B or %2F literals rather than the decoded character.
WebAuthn uses URL-safe Base64 for challenge values and credential IDs transmitted between the browser and the relying party server.5 The Web Authentication API returns ArrayBuffer objects; the server receives them as URL-safe Base64 strings in JSON. Converting an ArrayBuffer to URL-safe Base64 in a browser requires the Uint8Array intermediary: encode with btoa(String.fromCharCode(...new Uint8Array(buffer))), then apply the character substitution and strip padding.
Converting between variants across common languages
Converting a standard Base64 string to URL-safe requires only a text substitution; no re-encoding of the underlying bytes is necessary because the two representations encode identical data. In Python: url_safe = standard.replace('+', '-').replace('/', '_').rstrip('='). In JavaScript: const urlSafe = standard.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''). In Go: strings.NewReplacer("+", "-", "/", "_", "=", "").Replace(standard). All three produce equivalent output for the same input, and the same substitution rules apply in every other language that supports a simple character replacement on a string.
Applying the substitution before transport
The reverse direction, converting URL-safe back to standard before decoding, requires restoring the characters and adding padding. In Python: standard = (url_safe.replace('-', '+').replace('_', '/') + '=' * (-len(url_safe) % 4)). Adding padding before the character substitution avoids a subtle off-by-one error when the padding calculation depends on the original string length. Match the padding convention to what your target decoder expects, because some decoders reject input with too much padding while other decoders require the correct amount of padding before they will attempt to decode.
Keep conversion close to the boundary where the value leaves your application. If a helper returns URL-safe Base64, name it base64Url and document that callers must not pass it to standard MIME or PEM decoders. That single contract prevents accidental mixing across HTTP headers, JWT libraries, and storage keys. Encoding the same payload with different variants at different layers of the stack produces values that look similar but decode to different bytes, so the contract must be enforced consistently across every component that handles the value. Converting between standard and URL-safe alphabets works directly in the browser, so you can generate a URL-safe value or convert an existing standard one without writing a one-off script for a single conversion.
When to use this
Use URL-safe Base64 for JWTs, OAuth PKCE, signed URLs, cookie values, and any Base64 string embedded in a URL or HTTP header. Use standard Base64 for PEM files, MIME attachments, and data URIs.
Examples
Convert standard Base64 to URL-safe in JavaScript
const standard = btoa('Hello+World/Test'); const standard = btoa('Hello+World/Test');
const urlSafe = standard.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
// Use urlSafe in URL query strings and JWT headers The reverse: urlSafe.replace(/-/g, '+').replace(/_/g, '/') + '=='.slice((urlSafe.length + 3) % 4)
Python: choose the right encoder for each context
import base64 encoded = base64.b64encode(data)
import base64 # For email attachments, data URIs, PEM files: encoded = base64.b64encode(data) # For JWTs, URLs, cookie values: encoded = base64.urlsafe_b64encode(data).rstrip(b'=')
base64.urlsafe_b64encode() retains = padding , strip manually when needed.
- 1.
"Percent-encoding," url.spec.whatwg.org, accessed June 2026. https://url.spec.whatwg.org/#percent-encoding
- 2.
B. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648
- 3.
N. Sakimura, N. Bradley, and J. Jones, "JSON Web Token (JWT)," RFC 7519, IETF, May 2015. https://datatracker.ietf.org/doc/html/rfc7519
- 4.
D. Waite and A. Parecki, "Proof Key for Code Exchange by OAuth Public Clients," RFC 7636, IETF, September 2017. https://datatracker.ietf.org/doc/html/rfc7636
- 5.
"Web Authentication: An API for accessing Public Key Credentials," w3.org, accessed June 2026. https://www.w3.org/TR/webauthn-2/