โ† Back to DevDecoder

Base64 vs. URL encoding: when to use which

A practical guide for developers ยท Covers Base64, Base64URL, and percent-encoding

Base64 and URL encoding (also known as percent-encoding) are two of the most commonly used text transforms in web development. They look superficially similar โ€” both are ways of representing arbitrary data using a restricted set of characters โ€” but they solve different problems and are not interchangeable. Mixing them up causes a surprising number of production bugs: mangled query parameters, broken download URLs, authentication failures where a token silently decodes to garbage. This guide walks through when each one is the right choice, the common ways developers get them wrong, and the edge cases that bite you in production.

What each one is actually for

Base64: binary โ†’ ASCII

Base64 takes arbitrary bytes (including non-printable ones) and represents them using 64 printable ASCII characters: Aโ€“Z, aโ€“z, 0โ€“9, plus + and /, with = used for padding. Every three input bytes become four output characters. That means the output is about 33% larger than the input, but โ€” crucially โ€” it's pure ASCII text that can be dropped into any medium that accepts text: JSON values, email bodies, HTTP headers, XML, log files.

The canonical use cases are:

URL encoding: reserved characters โ†’ %HH

URL encoding (RFC 3986 percent-encoding) replaces characters that have structural meaning in a URL, or that aren't safe in a URL, with % followed by the two-digit hexadecimal byte value. A space becomes %20, a / becomes %2F, a ? becomes %3F, and so on. Any byte outside the unreserved set (Aโ€“Z, aโ€“z, 0โ€“9, and -_.~) either must be or may be percent-encoded depending on context.

You use URL encoding when inserting a value into one of the structural positions in a URL: a path segment, a query string parameter, or a fragment. The goal is to prevent characters inside your value from being interpreted as URL structure. If a query parameter's value contains an &, and you don't encode it, the server will interpret the rest of the value as the next parameter.

The mental model: different alphabets, different goals

Base64 is a total transform: it re-encodes every byte in the input, producing output that happens to be URL-unsafe-but-ASCII. URL encoding is a partial transform: it only touches characters that are unsafe in a URL context, and leaves normal letters and digits alone.

The best way to remember the distinction:

Base64URL: the variant that matters

Standard Base64 uses + and / as two of its 64 characters. Both have structural meaning in URLs (/ is a path separator; + is a space in application/x-www-form-urlencoded). If you put a standard Base64 string into a URL, something will eventually interpret it wrong.

RFC 4648 defines Base64URL to fix this: use - instead of +, and _ instead of /. Padding = characters are usually stripped as well, since they also need to be percent-encoded in URLs (= is the query-param separator). Base64URL is what JWTs use. When you paste a JWT into a decoder, the - and _ you see are part of this variant.

If you take a Base64URL string and run it through standard Base64 decoding, many libraries will throw an error on the - and _. Conversely, a standard Base64 string with + or / can silently corrupt when passed through a URL.

Common bugs from mixing them up

Double-encoding query parameters

Suppose you want to pass the value hello world as a query parameter. You run it through encodeURIComponent and get hello%20world. You then โ€” because you've seen Base64 used for tokens โ€” decide to "be safe" and Base64-encode the encoded form. Now the other end has to know to Base64-decode and URL-decode, in the right order. The more common error is the reverse: the other end URL-decodes once, gets a Base64 string, decodes that, gets bytes, and gets confused when those bytes aren't what was sent originally. Don't stack encodings without a reason.

Forgetting to URL-encode Base64 in a URL

If you generate a standard Base64 string like abc+def/ghi= and put it in a URL, three things will bite you:

The fix is either (a) use Base64URL instead, or (b) percent-encode the whole Base64 string before putting it in the URL. Both work; Base64URL is tidier.

Encoding the whole URL instead of just a component

A classic mistake:

// wrong โ€” mangles : and / in the URL itself
encodeURIComponent('https://example.com/api?q=hello world')
// โ†’ "https%3A%2F%2Fexample.com%2Fapi%3Fq%3Dhello%20world"

That's the right output only if the URL is itself a value being passed as a redirect target or OAuth parameter. If you just want a working URL, only encode the value of each query parameter:

const q = encodeURIComponent('hello world');
const url = `https://example.com/api?q=${q}`;
// โ†’ "https://example.com/api?q=hello%20world"

encodeURI vs. encodeURIComponent

JavaScript ships two URL-encoding functions, and the choice between them trips up most developers at least once:

Rule of thumb: unless you are literally taking an already-assembled URL and preparing it for a context where only the non-reserved characters need escaping, use encodeURIComponent.

When to reach for which: a cheat sheet

SituationUse
Embedding binary data (image, cert, file) in JSON or HTMLBase64
HTTP Basic auth headerBase64
Data URI (data:image/png;base64,...)Base64
Inside a URL path or query valueURL encoding (via encodeURIComponent)
JWT parts, OAuth state parameters, share linksBase64URL (no padding)
Form POST body (application/x-www-form-urlencoded)URL encoding per-value
Email subject line with special charactersRFC 2047 encoding (a Base64 variant)
MIME email attachmentsBase64

A brief word on what neither of these is

Neither Base64 nor URL encoding is encryption. Both are reversible with zero secret input โ€” any observer who captures the encoded value can recover the original in one step. If you're trying to hide a value from someone who has the encoded form, you need actual cryptography: symmetric encryption with a key they don't have, or signed tokens whose payload is public but whose authenticity you can verify.

This is worth emphasizing because "obfuscation by Base64" keeps showing up in real codebases: API keys in a config file, JWT payloads treated as if they were secret, Base64-wrapped IDs used as access tokens. Every one of those is an incident waiting to happen.

Test your understanding

If you want to internalize the difference, grab any string and try it through both on DevDecoder. Notice that Base64 encoding always transforms every character (even plain ASCII), while URL encoding leaves normal letters and digits untouched. That asymmetry is the whole story.