DEV Community

ZahrionTech
ZahrionTech

Posted on

Base64URL Explained: Encoding Data for URLs and JWTs

You've probably seen Base64 before — those long strings of letters, numbers, +, /, and = padding. It works fine in many places, but the moment you drop that string into a URL or a filename, things break. + turns into a space, / looks like a path separator, and = confuses query strings.

That's exactly the problem Base64URL solves.

What is Base64URL?

Base64URL is a variant of Base64 designed for contexts where encoded data must survive inside URLs, filenames, or cookies without any escaping. It keeps the same core idea — representing binary data as printable ASCII text — but swaps out the two problematic characters and drops the padding:

  • + becomes -
  • / becomes _
  • = padding is omitted

So where standard Base64 might produce a+b/c==, the Base64URL version of the same data is a-b_c. Same information, safe to paste anywhere.

Where you'll meet it

The most common place developers run into Base64URL is JSON Web Tokens (JWTs). A JWT has three parts separated by dots — header, payload, and signature — and each part is Base64URL-encoded. If you've ever pasted a token into a debugger to inspect its payload, you were decoding Base64URL.

It also shows up in OAuth state parameters, PKCE code challenges, WebAuthn attestation data, and anywhere else binary data needs to travel inside a URL.

Encoding and decoding in JavaScript

The browser's built-in btoa() and atob() only handle standard Base64. Converting to and from Base64URL is just a character swap — here are two small helpers:

// Standard Base64 -> Base64URL
function toBase64Url(base64) {
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// Base64URL -> Standard Base64 (then decode)
function fromBase64Url(base64url) {
  let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
  // Restore the missing padding
  base64 += '='.repeat((4 - base64.length % 4) % 4);
  return atob(base64);
}

// Example: round-trip some text
const encoded = toBase64Url(btoa('hello world')); // "aGVsbG8gd29ybGQ"
console.log(fromBase64Url(encoded));              // "hello world"
Enter fullscreen mode Exit fullscreen mode

The padding trick works because Base64 always operates in groups of four characters, so you can reliably re-add the missing = signs before decoding. That's why Base64URL can safely leave them out.

Decoding a JWT payload

Reading a token's claims is a handy real-world use of this. Take the middle segment of a JWT and run it through fromBase64Url:

function decodeJwtPayload(token) {
  const payload = token.split('.')[1];
  return JSON.parse(fromBase64Url(payload));
}

// Example
const token = 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiemFocmlvbiJ9.signature';
console.log(decodeJwtPayload(token)); // { user: 'zahrion' }
Enter fullscreen mode Exit fullscreen mode

One important reminder: decoding a payload is not the same as verifying it. Anyone can read the claims — only checking the signature proves the token is authentic.

Wrapping up

Base64URL is a small but important detail: the same Base64 alphabet, minus the characters that break URLs. If you're debugging tokens, inspecting OAuth flows, or just curious what an encoded string actually says, a quick decoder saves you the manual character-swapping. I keep ToolStack Tools bookmarked for exactly that — it's a free, no-signup Base64 encoder and decoder that runs entirely in your browser, so pasting in a token or an encoded string stays private.

Top comments (0)