How to URL-encode query strings safely

Base64 and encoding 9 min read

How to URL-encode query strings safely

July 31, 2026 · 9 min read

A query string is just text glued onto a URL with ? and &, which means any space, slash, or ampersand inside a value collides with the characters that give the URL its structure. A search term like news & weather pasted raw into ?q=news & weather doesn't fail loudly - it silently truncates at the space, or the server reads weather as a second, unrelated parameter.

Percent-encoding (RFC 3986) exists to solve exactly this: escape anything that isn't safe to appear literally in a URL, and leave everything else untouched.

Why raw text breaks URLs

Reserved characters - : / ? # [ ] @ ! $ & ' ( ) * + , ; = - and the plain space all carry structural meaning somewhere in a URL. A space in a query value can get interpreted as the end of the URL by some parsers; an unescaped & starts a new parameter; an unescaped # starts a fragment and everything after it silently never reaches the server at all.

This is why a URL copied out of a chat app sometimes "works" and the same URL pasted into a different context breaks - different consumers are more or less lenient about unescaped characters, but none of them are required to guess your intent correctly.

What percent-encoding actually does

Percent-encoding replaces each unsafe byte with % followed by its two-digit hex value. A space becomes %20, & becomes %26, / becomes %2F. Everything else - letters, digits, and a small set of always-safe characters - passes through unchanged, so the result stays mostly readable.

encodeURIComponent("news & weather"); // "news%20%26%20weather"
decodeURIComponent("news%20%26%20weather"); // "news & weather"

Encoding a component vs a whole URL

JavaScript gives you two different functions on purpose. encodeURIComponent escapes everything that isn't safe inside a single value - including /, ?, and & - which is what you want for a query parameter, a path segment, or a redirect target you're embedding inside another URL. encodeURI escapes far less, because it assumes you're encoding an entire URL and want to preserve its structural characters like / and ?.

The most common bug is using the wrong one: run a full URL through encodeURIComponent and you'll mangle the scheme and slashes into %3A%2F%2F; run a single query value through encodeURI and an embedded & will slip through unescaped, quietly breaking the next parameter.

URL encoding vs Base64

These solve different problems and aren't interchangeable. Base64 re-encodes arbitrary bytes into a fixed 64-character alphabet - the whole string changes shape, and every character costs the same regardless of input. Percent-encoding only escapes the handful of characters that are actually unsafe in a URL and leaves the rest as plain, readable text.

If you need to embed binary data (an image, a signed token) in a URL, Base64URL - Base64 with +// swapped for -/_ and padding stripped - is the right tool, since raw Base64's + and / are themselves unsafe in URLs. For plain text query values, percent-encoding is simpler and keeps the value legible in logs and browser history.

Common gotchas

Double-encoding is the most frequent real-world bug: a value gets encodeURIComponent'd once by your code and again by a framework or proxy, turning %20 into %2520. If a decoded value still has stray %XX sequences in it, that's usually the tell.

The other frequent surprise: + means a literal plus sign to encodeURIComponent/decodeURIComponent, but inside an application/x-www-form-urlencoded body (classic HTML form submissions), + means a space instead. Mixing up which encoding rules apply to query strings versus form bodies is a common source of "the space turned into a plus sign" bugs.

FAQ

Does URL encoding hide or protect sensitive data?
No. Percent-encoding is purely a compatibility transform, fully reversible by anyone - it is not encryption or obfuscation.
Why does my decoded string still have %20 in it?
The value was likely encoded twice. Decode it more than once, or fix the double-encoding at the source.
Should I encode the whole URL or just the query value?
Just the value. Encoding an entire URL with encodeURIComponent breaks its own structure by escaping the scheme and slashes.

Browse all tools