Base64 vs. Base64url 2026: The Small Difference With a Big Impact
Everyone who has ever inlined an image as a data URL or peeked inside a JWT has met Base64. What fewer people know is that it comes in two flavors: the standard alphabet with + and /, and the URL-safe variant Base64url with - and _. Mix them up and you get broken links, truncated filenames, and tokens the server rejects. This article explains how Base64 works, why the standard alphabet breaks in URLs, and when you absolutely need Base64url as defined in RFC 4648.
What Base64 actually does
Base64 is an encoding, not an encryption scheme. Its only job: rewrite arbitrary bytes so they can travel as plain ASCII text. That is needed anywhere a channel only accepts »printable« characters: email headers, JSON fields, XML, URLs, or a data URL in HTML.
The principle is simple. Base64 takes the byte stream and splits it into blocks of 3 bytes. Three bytes are 24 bits. Those 24 bits are not read in groups of 8, but in four groups of 6 bits. Each 6-bit group can hold a value from 0 to 63 – and the alphabet that maps each value to a printable character contains exactly 64 characters. So 3 bytes become 4 characters.
Hence the well-known overhead: four output characters for three input bytes, a 4:3 ratio, meaning roughly 33 % more data. A 900 KB image becomes about 1.2 MB as Base64. That is why inlining assets via data URLs only pays off for small files.
The standard alphabet: A–Z, a–z, 0–9, + and /
RFC 4648, the authoritative standard from 2006, defines the classic Base64 alphabet in Section 4. The first 26 values (0–25) are the uppercase letters A–Z, the next 26 (26–51) the lowercase letters a–z, then the digits 0–9 for values 52 to 61. Two slots remain: value 62 is the plus sign +, value 63 the slash /.
A concrete example makes it tangible. The three bytes of the word Man (ASCII 77, 97, 110) become the string TWFu in Base64. That is the textbook case with no remainder. Likewise, the classic Hello, World! becomes SGVsbG8sIFdvcmxkIQ== – with the two equals signs at the end, which we will get to shortly.
Important: those two special characters + and / are the whole point of contention. In plain text they are entirely harmless, but they cause trouble the moment the result travels into a URL or a filename.
Padding with the equals sign
Base64 works in 3-byte blocks – but not every file is a clean multiple of three. If one or two bytes remain at the end, the encoder pads with zero bits and marks the gap with the padding character =. With 1 byte left over you get two output characters plus ==; with 2 bytes it is three characters plus a single =.
The = carries no data itself. It only ensures the output is always a multiple of four characters long – handy when several Base64 blocks are concatenated. M (1 byte) becomes TQ==, Ma (2 bytes) becomes TWE=, and Man (3 bytes) becomes the familiar TWFu with no padding at all.
Strictly speaking a decoder does not need the padding: the number of missing = can be derived uniquely from the string length. Length modulo 4 equal to 0 means no padding, equal to 2 means two = are missing, equal to 3 means one is missing. Exactly this calculation lets you drop the padding in many contexts – more on that shortly.
Why + and / break in URLs
URLs have their own grammar, laid down in RFC 3986. In that grammar several characters already carry a fixed meaning – and the two Base64 special characters are among them. The slash / separates path segments. Put a / in the middle of a Base64 string inside a URL path and the server reads a directory boundary where data should be.
The plus sign is even trickier. In the query-string encoding of form data (application/x-www-form-urlencoded), a + stands for a space. Drop a Base64 value with a + into a query parameter and the receiving end may turn it into a space – the value is destroyed before anything is even decoded. The = collides too, because in query strings it separates key and value (key=value).
The classic workaround is percent-encoding: + becomes %2B, / becomes %2F, = becomes %3D. It works, but it bloats the string and is error-prone. In filenames it does not help at all, because / is simply forbidden there on many systems. So a cleaner solution is needed.
Base64url per RFC 4648 Section 5
That solution lives in Section 5 of the very same RFC 4648 and is officially called »Base 64 Encoding with URL and Filename Safe Alphabet«, or Base64url for short. The idea is minimally invasive: the procedure stays bit-for-bit identical, only the two problematic characters are swapped. Value 62 is now the minus sign - instead of +, value 63 the underscore _ instead of /.
Both replacement characters are safe in URLs and filenames. The result can be placed into a path, a query parameter, or a filename without percent-encoding. The standard Base64 a+b/c9== would become a-b_c9 in Base64url. Many libraries implement Base64url simply as standard Base64 followed by swapping + and /.
For padding, Base64url usually goes one step further and drops the = entirely. That is possible because – as shown above – the padding length can be reconstructed from the string length. Some contexts even forbid the padding explicitly. A robust decoder should accept both cases: with and without =.
Where Base64url actually gets used
The most prominent place is the JSON Web Token (JWT). A JWT consists of three dot-separated parts: header, payload, and signature. All three are encoded as Base64url without padding – precisely because a token often travels in the Authorization header, but also in URLs or cookies. A + or / in the middle would reliably break something when the token is passed along.
Anyone who has taken a JWT apart by hand knows the pattern: the middle part between the dots is the payload. Paste it into a standard Base64 decoder and it may choke on the missing padding or on a - it does not recognize. You have to treat it as Base64url. The same holds for the JSON Web Key (JWK): parameters like the modulus and exponent of an RSA key are stored as Base64url strings (field type base64url).
More places: WebAuthn encodes challenge, credential ID, and attestation data as Base64url because they travel through JSON and browser APIs. Data URLs mostly use standard Base64, but the moment such a URL is embedded into a query parameter, Base64url is the more reliable choice. And in many APIs, ID tokens, reset links, or confirmation codes are Base64url-encoded random bytes.
Base64 is not encryption
The biggest misconception around Base64 – standard or url variant alike – is the belief that it offers protection. It does not. Base64 is a fully reversible, public rewriting. There is no key, no secret, nothing. Anyone who sees the string can turn it back in seconds – any browser, any command-line tool, any online decoder can do it.
This is a common trap with JWTs: a token's payload is readable, not encrypted. It contains claims like user ID, roles, or expiry in plain text – just Base64url-encoded. What a JWT secures is only the signature in the third part: it proves that no one altered the header or payload afterwards. It does not provide confidentiality. Put sensitive data into a JWT payload and you are effectively exposing it.
Rule of thumb: Base64 provides transportability, not secrecy. To protect data you need encryption (such as AES or TLS). To secure integrity you need a signature or an HMAC. Base64 does neither.
Common pitfalls in practice
The classic is confusing the alphabets. A service returns Base64url, your decoder expects standard Base64 – and it stumbles over a - or _ that has no value in the classic alphabet. The reverse is just as true: a + from standard Base64 is invalid under a strict Base64url check. Many libraries offer separate functions (base64_encode vs. a url-safe variant); grabbing the wrong one is a one-line bug with a long hunt.
The second pitfall is padding. A Base64url string without = has a length modulo 4 of 2 or 3 – a decoder that stubbornly expects a multiple of four then throws an error. The clean fix: add the missing = based on the length before decoding, or use a tolerant decoder.
Third point: line breaks. The older MIME Base64 (RFC 2045) inserts a break every 76 characters. Such breaks have no place in URLs, JWTs, or JSON – a \n in the middle of a token is a sure path to an invalid value. Make sure your encoding runs without line breaks when the result goes into a compact context.
Deciding quickly: standard or url?
The rule of thumb is manageable. As long as your Base64 stays in a »wide« text channel – an email body, an XML field, a config file, a data URL in an HTML attribute – standard Base64 with +, / and = is the right and widely expected choice. Nothing gets in the way, and most tools expect exactly this alphabet.
But the moment the value travels into a URL, a query parameter, a filename, or a JWT, you use Base64url – with -, _ and usually no padding. That saves you percent-encoding, avoids the space problem of + and the path confusion of /. And the third rule holds for both variants: Base64 is not protection – it is the envelope, not the lock.
How tools on CalcSI help
To convert bytes safely in both directions, use the Base64 Encoder/Decoder: it shows you at once how text turns into the encoded string – including +, / and padding – and decodes both standard and url variants back. To check how a value looks in an address bar, make it readable with the URL Encoder/Decoder and see which characters would be percent-encoded and why - and _ slip through unnoticed. If the Base64url part sits inside a token, take it apart with the JWT Decoder into header, payload, and signature, and see in black and white that the payload is only encoded, not encrypted. And to be sure a decoded file is unchanged, build a checksum over the original with the Hash Generator. All four run entirely in the browser – your data never leaves your machine.
Comments
Comments are powered by Disqus. Before they load, we need your consent — Disqus is a third-party service and sets its own cookies.