toolhq.io

All posts
July 14, 20265 min read

URL-safe Base64 (base64url): what it is and how to encode and decode it

Standard Base64 looks harmless until you drop it into a URL. Three of its characters, +, /, and =, collide with characters that URLs treat as structure, so the value arrives at the other end mangled. The fix is a small variant of the alphabet called base64url, defined in RFC 4648 section 5, and it is what JWTs, JWKs, and most web tokens actually use. The Base64 Encode / Decode tool handles both forms, with correct UTF-8 handling and a URL safe option, entirely in your browser.

A quick recap of standard Base64

Base64 turns arbitrary bytes into printable text using an alphabet of 64 characters: the letters A to Z and a to z, the digits 0 to 9, and finally + and /. Every 3 input bytes become 4 output characters. When the input length is not a multiple of 3, the output is padded with one or two = characters so its length is always a multiple of 4. Note that this is only an encoding, a reversible change of representation; it hides nothing and secures nothing, as Base64 is not encryption explains.

Why standard Base64 breaks in URLs

Each of the three special characters conflicts with URL syntax in its own way:

  • + turns into a space. In form-encoded query strings, + is the legacy encoding for a space. A server that decodes the parameter replaces every + in your token with a space, and the token no longer matches.
  • / is a path separator. A Base64 value placed in a URL path gets split into multiple path segments, and routers either 404 or hand your handler only part of the value.
  • = is reserved in query strings. It joins keys to values, so trailing padding is ambiguous at best and stripped by intermediate software at worst.

You can percent-encode the value to survive the trip, but that invites double-encoding bugs when one layer encodes and another decodes. Changing the alphabet is simpler.

The base64url variant

RFC 4648 section 5 defines a URL and filename safe alphabet. Only two characters change, and padding is usually dropped:

PurposeStandard Base64base64url
Alphabet value 62+-
Alphabet value 63/_
Padding=usually omitted

The other 62 characters are identical, so most of any encoded string looks the same in both forms.

Where base64url shows up

Once you know the alphabet, you start recognizing it everywhere: the three segments of a JWT (header, payload, and signature) are each base64url without padding; the byte fields of a JSON Web Key such as n, e, and k; session and password reset tokens in query parameters; and generated file names, where / would be illegal on disk. If a token contains - or _ but never + or /, it is almost certainly base64url.

Converting and decoding

Converting between the two forms is pure character substitution. To make a standard value URL safe, replace + with -, replace / with _, and strip the trailing =. To go back, reverse the substitutions and restore padding: append = until the length is a multiple of 4. A valid unpadded length is 0, 2, or 3 modulo 4; a remainder of 1 means the string is corrupt.

Padding matters because some decoders are strict. In JavaScript, atob only understands the standard alphabet, so translate the characters first. Node accepts Buffer.from(value, "base64url") directly and tolerates missing padding.

A worked example

The two bytes FF EF encode in standard Base64 as /+8=, which manages to contain all three problem characters at once. Put that in a query string and the + decodes to a space, the = gets eaten, and the value that reaches your server is "/ 8" or worse. The same bytes in base64url are _-8: the / became _, the + became -, and the padding is gone. Nothing in that string means anything special to a URL parser, so it survives untouched.

To see both forms side by side on your own data, toggle the URL safe option in the Base64 tool; the conversion runs locally and nothing leaves your browser.