URL-Safe Base64 vs Standard Base64

URL-safe Base64 replaces + with - and / with _ so strings are safe in URLs, HTTP headers, and filenames. Learn when to use each variant and how to convert between them.

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.

Opens the Base64 Text & File Encoder/Decoder with the value from this page already filled in.

Open in the tool →

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

Before
const standard = btoa('Hello+World/Test');
After
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

Before
import base64
encoded = base64.b64encode(data)
After
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.

Sources
  1. 1.

    "Percent-encoding," url.spec.whatwg.org, accessed June 2026. https://url.spec.whatwg.org/#percent-encoding

  2. 2.

    B. Josefsson, "The Base16, Base32, and Base64 Data Encodings," RFC 4648, IETF, October 2006. https://www.rfc-editor.org/rfc/rfc4648

  3. 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. 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. 5.

    "Web Authentication: An API for accessing Public Key Credentials," w3.org, accessed June 2026. https://www.w3.org/TR/webauthn-2/

FAQ

Usually yes, but not reliably. If the string contains + or /, it is standard Base64. If it contains - or _, it is URL-safe Base64. However, a string with no + / - _ characters is ambiguous. Never silently auto-detect; document the variant in your API.

No , and most protocols that use URL-safe Base64 strip padding explicitly. JWT, PKCE, and WebAuthn all prohibit = padding in URL-safe fields. Match the convention of the target protocol.

The JWT library will attempt to decode the header and payload using URL-safe Base64. + and / characters in a standard-Base64 string decode differently under the URL-safe alphabet, producing garbled JSON. The library will throw a parse error or silently use incorrect claim values.

Yes. RFC 4648 §5 defines the Base64url encoding with the - and _ alphabet. JWT is defined in RFC 7519, which specifies Base64url without padding per RFC 4648 §5.

Yes. MIME Base64 adds CRLF line wrapping at 76 characters. bcrypt uses a custom alphabet (./0-9A-Za-z) for password hashing. CapyToolkit keeps the standard and URL-safe variants separate so you can check which alphabet your API requires.

Additional resources

Guides

Embedding Images as Data URIs in HTML and CSS Learn how to embed images, SVGs, and fonts as Base64 data URIs in HTML attributes and CSS. Includes format, size limits, and when to avoid inlining. Building HTTP Basic Auth Headers with Base64 HTTP Basic Authentication encodes username:password in Base64 for the Authorization header. Learn the format, curl usage, and why HTTPS is required. How JWT Tokens Use Base64url Encoding JWT tokens encode header and payload as Base64url without padding. Learn the structure, how to decode the claims, and why JWTs are not encrypted by default. Base64 in Kubernetes Secrets Kubernetes Secrets store values as Base64-encoded strings in YAML. Learn how to encode, decode, and manage secrets , and why Base64 is not encryption. MIME Email Attachments and Base64 Encoding SMTP email encodes attachments as Base64 in MIME multipart messages. Learn the format, line length rules, charset headers, and common email encoding bugs. Storing Binary Secrets in Environment Variables Environment variables only hold text strings, but Base64 encoding lets you store binary secrets like TLS keys and certificates as env var values. Learn the pattern and risks. Embedding Binary Data in JSON APIs JSON cannot represent raw binary data. Base64 encoding is the standard way to embed images, files, and binary fields in JSON API requests and responses. URL-Safe Base64 vs Standard Base64 URL-safe Base64 replaces + with - and / with _ so strings are safe in URLs, HTTP headers, and filenames. Learn when to use each variant and how to convert between them. SSH Keys and PEM Certificates in Base64 SSH keys, TLS certificates, and PEM files wrap raw binary keys in Base64 between header and footer markers. Learn the format, how to decode keys, and common CI/CD patterns.