URL encoding is one of those things you learn once, use constantly, and never really think about until something breaks. Then you spend an hour figuring out why + in a query string turned into a space, or why an ampersand in a search term truncated your parameters.
Let's clear it up properly.
What URL encoding actually is
URLs can only contain a limited set of characters safely. The spec (RFC 3986) defines a small group of "unreserved" characters that are always allowed as-is: letters, digits, and a handful of symbols (-, ., _, ~). Everything else either has a special meaning in a URL or isn't allowed at all.
URL encoding — also called percent-encoding — replaces those problematic characters with a % followed by their hex byte value. A space becomes %20. An ampersand becomes %26. A forward slash becomes %2F.
Hello World! → Hello%20World%21
name=John&Jane → name%3DJohn%26JaneWhen you actually need it
The most common case is building query strings. If a value contains a character that means something structural in a URL — &, =, ?, # — you have to encode it, or the URL falls apart.
Say you're searching for the term "cats & dogs":
// Wrong — the & ends the parameter early
https://example.com/search?q=cats & dogs
// Right
https://example.com/search?q=cats%20%26%20dogsOther places encoding matters:
- Path segments that contain slashes, spaces, or non-ASCII characters.
- Form submissions using
application/x-www-form-urlencoded. - Redirect URLs passed as a query parameter (a URL inside a URL — a classic source of double-encoding pain).
- OAuth and API signatures, where the exact encoding is part of the signed payload.
The gotchas that bite people
1. Spaces: %20 vs +
In the path portion of a URL, a space is %20. In a query string using the form-encoded format, a space is often +. Both are valid in the right context, but they aren't interchangeable everywhere. If you decode a + as a literal plus sign when it should have been a space, your data is subtly wrong. JavaScript's encodeURIComponent always uses %20, which is the safer default.
2. encodeURI vs encodeURIComponent
This is the JavaScript trap. encodeURI is meant for encoding a whole URL, so it leaves characters like &, =, and ? alone — because those are supposed to appear in a valid URL. encodeURIComponent encodes a single component (like one query value) and escapes those characters too.
const value = "a=1&b=2";
encodeURI(value); // "a=1&b=2" (broken as a param)
encodeURIComponent(value); // "a%3D1%26b%3D2" (correct)Rule of thumb: use encodeURIComponent for individual values you're stitching into a URL, and reserve encodeURI for cases where you already have a full URL that just needs light cleanup.
3. Double-encoding
Encode something twice and you get garbage. A space becomes %20, then the % itself gets encoded to %25, giving you %2520. This happens constantly when a value passes through multiple layers — a client encodes, a framework encodes again, a proxy re-encodes. If you see %25 showing up where you didn't expect it, you're double-encoding somewhere.
4. Unicode and bytes
Percent-encoding works on bytes, not characters. Modern URLs use UTF-8, so a character like é (which is two bytes in UTF-8) encodes to %C3%A9. Older systems that assumed one byte per character produce broken output for anything non-ASCII. Make sure everything in your stack agrees on UTF-8.
A worked example
Let's build a URL to a maps service that takes a full address as a query parameter.
const address = "10 Downing St, London SW1A 2AA";
const base = "https://maps.example.com/search";
const url = `${base}?q=${encodeURIComponent(address)}`;
console.log(url);
// https://maps.example.com/search?q=10%20Downing%20St%2C%20London%20SW1A%202AANotice the comma became %2C and the spaces became %20. If we'd skipped encoding, the comma and spaces would have left the URL technically malformed, and some servers would reject it outright.
Decoding on the receiving end reverses it:
const raw = "10%20Downing%20St%2C%20London%20SW1A%202AA";
decodeURIComponent(raw);
// "10 Downing St, London SW1A 2AA"When you just need to check something
Half the time you don't need code at all — you're debugging a log line or an API response and want to see what a nasty-looking encoded string actually contains. Pasting it into a quick tool is faster than opening a REPL. I keep this URL Encoder / Decoder bookmarked for exactly that: drop in a string, see it decoded (or encoded) instantly, and spot double-encoding at a glance.
Quick reference
- Use
encodeURIComponentfor individual query values and path segments. - Use
encodeURIonly for a full URL that needs escaping without breaking its structure. - Spaces are
%20in paths;+only in form-encoded bodies. - If you see
%25where you expect a real character, suspect double-encoding. - Keep the whole pipeline on UTF-8 to avoid mangled non-ASCII characters.
The takeaway
URL encoding isn't complicated, but it's unforgiving. The bugs it causes are silent — no exception, just a value that's slightly wrong or a parameter that mysteriously vanishes. Pick the right function for the job, encode values not whole URLs, and watch for double-encoding. Get those three right and most of the pain disappears.
Photo by Chris Ried on Unsplash