Skip to content
Back to Blog
base64encodingcomparisondeveloper-tools

Base64URL vs Base64: What's the Difference?

Standard base64 breaks inside URLs because + and / are reserved. Base64URL fixes it with two character swaps and dropped padding. Here is exactly when you must use each, with a side-by-side decoder.

SZ
Founder, Molixa
11 min read
Share
Base64URL vs Base64: What's the Difference?

The difference between base64url vs base64 comes down to three small changes: standard base64 uses + and / in its alphabet plus = padding, while base64url swaps + for -, swaps / for _, and usually drops the = padding entirely. That is the whole spec-level difference. Everything else, the encoding math, the 6-bit grouping, the 33% size overhead, is identical between the two.

Why does it matter? Because +, /, and = all have reserved meanings inside URLs, query strings, and filenames. Drop a standard base64 string into a URL and + can be read as a space, / starts a new path segment, and = looks like a parameter assignment. Base64url exists so encoded data survives those contexts untouched. This guide covers exactly when each variant is required, the re-padding gotcha most articles skip, and why JSON Web Tokens use base64url for their header and payload.

Base64url vs Base64: The Core Difference#

Both variants are defined in RFC 4648. Standard base64 lives in Section 4; the URL- and filename-safe alphabet lives in Section 5. They encode bytes the same way: take three bytes (24 bits), split into four 6-bit groups, and map each group to one character from a 64-character alphabet. The only thing that changes is which characters fill the last two slots of that alphabet and whether you pad the end.

Here is the side-by-side that actually matters:

AspectStandard base64Base64url
Index 62 character+ (plus)- (hyphen/dash)
Index 63 character/ (slash)_ (underscore)
Padding= to a multiple of 4Usually omitted
Safe in URLs / query stringsNoYes
Safe in filenamesNo (the / breaks paths)Yes
RFC 4648 sectionSection 4Section 5
Output lengthIdenticalIdentical

Notice the first 62 characters (A-Z, a-z, 0-9) are the same in both alphabets. Only positions 62 and 63 differ. That is why converting between the two is a trivial character substitution and never requires re-encoding the original bytes.

Quick tip: if you ever see a base64-looking string with - or _ in it, you are almost certainly looking at base64url. Plain base64 never uses those two characters.

Why + and / break inside URLs#

A URL has a grammar, and several characters carry structural meaning. The slash / separates path segments. The plus + is historically interpreted as an encoded space in application/x-www-form-urlencoded data (the format used by query strings and form posts). The equals sign = separates a query parameter name from its value.

So if you put a standard base64 token like a+b/c= directly into a URL, a server might decode the + to a space, treat /c as a new path, and read = as the start of a value. Your data is now corrupted before your code ever touches it. Base64url sidesteps the whole problem by never emitting any of those three characters.

When You Must Use Each Variant#

The decision rule is short. Use base64url anywhere the output travels through a URL, a query parameter, an HTTP header, a filename, or a cookie. Use standard base64 for everything else: email attachments (MIME), data URIs, config files, JSON string values that are not URL-bound, and binary blobs stored in a database.

Use base64url when the encoded value will appear in:

  • A URL path or query string (for example ?token=...)
  • A JWT (header, payload, and signature are all base64url)
  • An OAuth or OpenID Connect parameter such as state or code_challenge
  • A filename or S3 object key (the / in standard base64 would create unintended folders)
  • WebPush keys, FIDO/WebAuthn credentials, and many other web crypto contexts

Use standard base64 when the value lives in:

  • An email attachment encoded as MIME
  • A data: URI embedded in HTML or CSS (these tolerate + and / fine)
  • A JSON or YAML config value that is never placed in a URL
  • A database column or log line

If you are not sure which you are holding, paste it into the free base64 encoder and decoder and decode it both ways. If only one variant produces clean, readable output, that tells you which alphabet produced it.

The Padding Gotcha Most Guides Skip#

This is the part that trips people up and the reason a copied snippet sometimes throws an error. Standard base64 pads the output with = so the total length is always a multiple of 4. Base64url normally strips that padding because = is itself unsafe in URLs.

The math: base64 encodes 3 bytes into 4 characters. When your input is not a multiple of 3 bytes, the encoder fills the gap with =. One leftover byte produces two padding chars (==), two leftover bytes produce one (=).

Here is the trap. Many strict decoders demand a length that is a multiple of 4. When base64url drops the padding, the string length is no longer a multiple of 4, so a naive decoder fails. The fix is to re-pad before decoding.

How to re-pad base64url before decoding#

To safely decode a base64url string with a generic base64 decoder, do two things: convert the alphabet back (- to +, _ to /), then add = until the length is a multiple of 4.

In JavaScript:

function base64urlDecode(input) {
  // 1. Restore the standard alphabet
  let b64 = input.replace(/-/g, '+').replace(/_/g, '/');
  // 2. Re-pad to a multiple of 4
  const pad = b64.length % 4;
  if (pad === 2) b64 += '==';
  else if (pad === 3) b64 += '=';
  else if (pad === 1) throw new Error('Invalid base64url length');
  return atob(b64);
}

A length remainder of 1 is impossible in valid base64, so treat it as a malformed input rather than guessing. If you skip the re-padding step, atob and most server-side decoders will reject the string or return garbage. Our base64 codec handles re-padding automatically, so you can drop in an unpadded base64url token and it decodes without you doing the arithmetic.

Why JWTs Use Base64url (Not Base64)#

A JSON Web Token is three base64url segments joined by dots: header.payload.signature. The reason it is base64url and not standard base64 is exactly the problem above. Tokens get passed in URLs, in Authorization: Bearer headers, and in query parameters during OAuth flows. A standard base64 token with a / or + in it would break the moment it hit a URL.

So the JWT spec (RFC 7519, building on RFC 7515) mandates base64url with padding removed. This is why a JWT never contains =, +, or /, only letters, digits, hyphens, underscores, and the two dot separators.

Warning: base64url is encoding, not encryption. Anyone can decode a JWT payload and read its claims. Never put secrets in a JWT body. The signature proves the token was not tampered with; it does not hide the contents.

If you want to inspect a token, you can pull apart the three segments and base64url-decode the header and payload with the JWT decoder tool. It does the alphabet conversion and re-padding for you so you see the raw JSON claims instantly. To understand what the signature segment proves versus what decoding alone shows, the breakdown in decode vs verify a JWT is worth a read before you trust any token in production.

Converting Between the Two Variants#

Because only two characters and the padding differ, conversion is pure string manipulation. You never re-run the base64 algorithm.

Standard base64 to base64url:

  • Replace every + with -
  • Replace every / with _
  • Strip trailing = characters

Base64url back to standard base64:

  • Replace every - with +
  • Replace every _ with /
  • Add = padding until the length is a multiple of 4

That symmetry is the practical takeaway: the two formats are the same data wearing different clothes. If a decode fails, the cause is almost always a mismatch (you fed base64url into a standard decoder without converting) or missing padding, not corrupted data.

A worked example#

Take the bytes for the string Hello?>. Standard base64 encodes it to SGVsbG8/Pg==. Notice the / at index 8 and the == padding. As base64url, the same bytes become SGVsbG8_Pg: the / is now _ and the padding is gone. Decode either one and you get Hello?> back. Same content, two safe-for-different-contexts representations.

Common Mistakes and How to Avoid Them#

A few errors show up again and again when developers mix the two variants:

  • Decoding base64url with a standard decoder and getting an error. Convert -/_ back and re-pad first. This is the single most common failure.
  • Assuming = is always present. Code that splits or validates on padding length breaks the moment it meets unpadded base64url. Make padding optional in your parsing.
  • URL-encoding a standard base64 string instead of using base64url. This works but bloats the output: + becomes %2B, / becomes %2F. Base64url is shorter and cleaner for URL contexts.
  • Treating base64 or base64url as a security layer. Both are reversible by anyone. Use them to transport binary as text, never to protect data.
  • Forgetting both formats carry the same 33% size overhead. Encoding 3 bytes as 4 characters means your output is roughly one-third larger than the raw input, in either variant.

If you are wrangling encoded binary like images alongside text, the guide on how to encode and decode base64 images and data URLs covers the data-URI side where standard base64 is the right call.

Frequently Asked Questions#

Is base64url the same as base64? No, but they are close. Base64url uses the exact same encoding algorithm and produces the same length output. The only differences are that + becomes -, / becomes _, and the = padding is typically dropped so the string is safe to use in URLs and filenames.

Why does base64url remove the padding? The padding character = has a reserved meaning in URLs (it separates a query parameter name from its value), so leaving it in could corrupt the data or confuse a server. Base64url drops it for safety. The downside is that strict decoders may need you to re-add the padding before decoding.

Can I decode base64url with a normal base64 decoder? Yes, but only after two steps. First convert the alphabet back by replacing - with + and _ with /. Then re-pad the string with = until its length is a multiple of 4. Skip either step and most standard decoders will throw an error or return garbage.

Why do JWTs use base64url instead of standard base64? Because tokens travel through URLs, HTTP headers, and OAuth parameters where +, /, and = would break. The JWT spec mandates base64url with no padding, which is why you never see those three characters in a valid JSON Web Token, only letters, digits, hyphens, underscores, and dot separators.

Is base64url encryption or just encoding? It is only encoding. Anyone can reverse it with no key, exactly like standard base64. It exists to represent binary data as URL-safe text, not to keep anything secret. Never store passwords, API keys, or other sensitive data as base64url and assume it is protected.

Does base64url produce a smaller string than base64? Slightly, only because it usually omits the = padding (saving zero to two characters). The encoded body is the same length in both. Both variants add the same roughly 33% overhead versus the raw binary, since every 3 bytes become 4 characters.

Base64url vs base64 is not a deep mystery once you see it as one alphabet with two swapped characters and optional padding. Reach for base64url whenever your data touches a URL, header, filename, or JWT, and standard base64 for MIME, data URIs, and storage. When a decode fails, suspect a variant mismatch or missing padding first, then drop the string into the base64 encoder and decoder to confirm which alphabet you are holding.

base64encodingcomparisondeveloper-tools

More from Molixa

Try Molixa Tools

50+ free AI tools for content creation, SEO, coding, and more. No signup, no watermark.

Explore all tools