base64.urlsafe_b64encode / urlsafe_b64decode

The base64url alphabet of RFC 4648: - replaces + and _ replaces /. Python keeps the = padding on encode and requires it on decode — JWTs and most URL tokens strip it.

base64 functionPython 2.4+Live demo
Common call
base64.urlsafe_b64encode(data).rstrip(b'=')
Returns
bytes such as b'-_-_'
Replaces
b64encode followed by urllib.parse.quote, or manual replace("+", "-")
Watch out
Padding stays on encode and is required on decode
base64.urlsafe_b64encode(ss — The data to encode, or the URL-safe Base64 text to decode. The decoder also accepts + and /.type: bytes-like (encode) · bytes-like | ASCII str (decode) · required)
→ bytes

Demo

Live evaluation
The same bytes in both alphabets. They differ only where the standard output has + or /.
Try:
Inputs
textstrany text
Code
import base64
data = '???>>>'.encode()
(base64.b64encode(data), base64.urlsafe_b64encode(data))
Result
(b'Pz8/Pj4+', b'Pz8_Pj4-')

JWTs use base64url with the padding removed, so a payload whose length is not a multiple of 4 raises Incorrect padding until you add -len(s) % 4 = signs back. The decoder translates - and _ to + and / and then runs the normal lenient decoder, which is why standard input such as "+/+/" decodes too. In the non-ASCII payload, "Wm_D" is where the URL-safe _ stands in for the / of standard Base64.

Parameters

NameTypeRequiredDescription
sbytes-like (encode) · bytes-like | ASCII str (decode)yesThe data to encode, or the URL-safe Base64 text to decode. The decoder also accepts + and /.

Return value

bytes — Encode: URL-safe Base64 as ASCII bytes, = padding kept. Decode: the original bytes.

Common patterns

URL token without padding
The common base64url form: URL-safe alphabet, no trailing =.
import base64, secrets
token = base64.urlsafe_b64encode(secrets.token_bytes(16)).rstrip(b'=').decode('ascii')
Decode a JWT payload (no signature check)
Split on dots, pad the middle part, decode, parse the JSON. This does NOT verify the token — use a JWT library for that.
import base64, json
payload = token.split('.')[1]
claims = json.loads(base64.urlsafe_b64decode(payload + '=' * (-len(payload) % 4)))
Base64url helpers
Encode without padding, decode with it restored.
import base64
def b64url_encode(b):
    return base64.urlsafe_b64encode(b).rstrip(b'=').decode('ascii')

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

Examples

1. - and _ instead of + and /
import base64 base64.urlsafe_b64encode(b'\xfb\xff\xbf')
Returns
b'-_-_'
2. Standard alphabet, same bytes
import base64 base64.b64encode(b'\xfb\xff\xbf')
Returns
b'+/+/'
3. Padding is kept
import base64 base64.urlsafe_b64encode(b'\xff\xff')
Returns
b'__8='
4. Strip it yourself
import base64 base64.urlsafe_b64encode(b'\xff\xff').rstrip(b'=')
Returns
b'__8'
5. Decode
import base64 base64.urlsafe_b64decode('-_-_')
Returns
b'\xfb\xff\xbf'
6. Standard input works too
import base64 base64.urlsafe_b64decode('+/+/')
Returns
b'\xfb\xff\xbf'
7. JWT header
import base64 base64.urlsafe_b64decode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
Returns
b'{"alg":"HS256","typ":"JWT"}'

Pitfalls

1. Decoding a JWT part as-is
JWT segments have their = padding removed. Add it back before decoding.
as-is
import base64
base64.urlsafe_b64decode('eyJzdWIiOiIxMjMifQ')
binascii.Error: Incorrect padding
padded
import base64
s = 'eyJzdWIiOiIxMjMifQ'
base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
b'{"sub":"123"}'
2. Decoding URL-safe text with b64decode
b64decode drops - and _ as junk, so the bytes come out wrong — or empty — without any error.
b64decode
import base64
base64.b64decode('-_-_')
b''
urlsafe_b64decode
import base64
base64.urlsafe_b64decode('-_-_')
b'\xfb\xff\xbf'
3. Expecting a str from the encoder
Like every encoder in the module it returns bytes; putting it in an f-string shows b'...'.
f-string of bytes
import base64
f"/files/{base64.urlsafe_b64encode(b'id-7')}"
"/files/b'aWQtNw=='"
.decode()
import base64
f"/files/{base64.urlsafe_b64encode(b'id-7').decode()}"
'/files/aWQtNw=='

When to use

Use it
  • Tokens in URLs and query strings, file names, cookie values
  • JWT / JOSE (base64url, padding stripped)
Reach for something else
  • APIs that specify plain Base64 → b64encode
  • Verifying JWTs — decoding is not validating; use a JWT library

Notes

CPython impl
b64encode(s).translate(+/ → -_) to encode; translate(-_ → +/) then b64decode(s) to decode — so decoding is lenient and has no validate option
Padding
= is not part of the URL-safe alphabet and already means key=value in query strings, which is why base64url usually strips it
Spec
RFC 4648 section 5, "Base 64 Encoding with URL and Filename Safe Alphabet"

FAQ

Take the middle part (token.split('.')[1]), add '=' * (-len(part) % 4), call base64.urlsafe_b64decode and json.loads the result. That only reads the claims — it does not check the signature.