Article · Feb 23, 2026 · 7 min read

Ten common URL encoding mistakes

I’ve seen the same URL encoding mistakes appear in production code from junior developers to senior engineers, in every language, at companies of every size. Most of them come down to a small set of patterns. Here are the ten most common, ranked by how often I see them, with the actual fix for each.

1. Encoding the whole URL instead of its values

The most common mistake. Easy to make, easy to spot.

// Wrong
const url = encodeURIComponent("https://api.example.com/search?q=hello");
// "https%3A%2F%2Fapi.example.com%2Fsearch%3Fq%3Dhello"
// No longer a URL — it’s an encoded blob.

// Right
const base = "https://api.example.com/search";
const url = `${base}?q=${encodeURIComponent("hello")}`;

The rule: encode values (data parts), not the URL itself.

2. Double-encoding

Already covered in detail in its own article, but worth restating:

// Wrong — value already encoded, then encoded again
const alreadyEncoded = "Hello%2C+World";
const url = `?q=${encodeURIComponent(alreadyEncoded)}`;
// "?q=Hello%252C%2BWorld"   ← double-encoded

The rule: encode exactly once, at the moment you put the value into a URL. Never store pre-encoded values.

3. Using encodeURI when you meant encodeURIComponent

// Wrong
const search = encodeURI("rock & roll");
// "rock%20&%20roll"   ← the & is still literal!
const url = `/search?q=${search}`;
// "/search?q=rock & roll" — server reads two parameters

// Right
const search = encodeURIComponent("rock & roll");
// "rock%20%26%20roll"
const url = `/search?q=${search}`;

The rule: in JavaScript, default to encodeURIComponent. Use encodeURI only when you specifically need to preserve reserved characters.

4. Forgetting to encode user input before concatenating

// Wrong
const url = `/search?q=${userInput}`;
// Breaks if userInput contains &, =, ?, #, or %

// Right
const url = `/search?q=${encodeURIComponent(userInput)}`;

The rule: any time you put user-provided data into a URL, encode it. Don’t trust that “just text” values are safe.

5. Mixing path and query encoding styles

// Wrong — putting form-encoded value (+ for space) into a path
const segment = "John+Doe";
const url = `/users/${segment}/profile`;
// Server reads user name as literally "John+Doe", not "John Doe"

// Right
const name = "John Doe";
const segment = encodeURIComponent(name);   // produces "John%20Doe"
const url = `/users/${segment}/profile`;

The rule: in path segments, use %20 for spaces (not +). The form-encoded convention only applies to query strings.

6. Using JavaScript’s deprecated escape() function

// Wrong — uses non-standard %uXXXX format for Unicode
escape("café")
// "caf%E9"   ← Latin-1, not UTF-8 — wrong!

escape("☺")
// "%u263A"   ← non-standard format, breaks URL parsers

// Right
encodeURIComponent("café")  // "caf%C3%A9"
encodeURIComponent("☺")     // "%E2%98%BA"

The rule: never use escape(). It’s been deprecated for over 20 years and produces invalid URL encoding for any non-ASCII character.

7. Manual string-replacement “encoding”

// Wrong — manual approach misses most cases
const url = `/search?q=${userInput.replace(' ', '%20')}`;
// Handles space but ignores: &, =, ?, #, +, special chars, unicode

// Right
const url = `/search?q=${encodeURIComponent(userInput)}`;

The rule: always use the standard library function. Manual encoding is almost always incomplete.

8. Forgetting that + means space in query strings

// Server sees: ?search=C%2B%2B+tutorial
// User typed: "C++ tutorial"
// Encoded with: URLSearchParams (which uses + for space)

const params = new URLSearchParams(window.location.search);
console.log(params.get("search"));
// "C++ tutorial"   ← URLSearchParams correctly decodes + as space

// But if you use decodeURIComponent directly:
console.log(decodeURIComponent("C%2B%2B+tutorial"));
// "C++ +tutorial"   ← wrong, decodeURIComponent treats + as literal

The rule: for query strings, use URLSearchParams. It handles +-as-space correctly. decodeURIComponent alone doesn’t.

9. Storing URL-encoded values in a database

// Wrong — encoding on write
db.users.insert({ name: encodeURIComponent("Smith & Sons") });
// Stores: { name: "Smith%20%26%20Sons" }

// Later, building a URL:
const link = `/companies/${encodeURIComponent(user.name)}`;
// "/companies/Smith%2520%2526%2520Sons"   ← double-encoded

The rule: store raw values in your database. Encode only at output time, immediately before assembling the URL.

10. Not encoding embedded URLs

// Setting up an OAuth callback
const targetUrl = "https://myapp.com/callback?token=abc";

// Wrong — outer URL parser sees two query strings
const authUrl = `https://auth.example.com/login?redirect=${targetUrl}`;
// "https://auth.example.com/login?redirect=https://myapp.com/callback?token=abc"

// Right — encode the inner URL fully
const authUrl = `https://auth.example.com/login?redirect=${encodeURIComponent(targetUrl)}`;
// "https://auth.example.com/login?redirect=https%3A%2F%2Fmyapp.com%2Fcallback%3Ftoken%3Dabc"

The rule: when a URL is a value in another URL, encode the inner one fully. Otherwise the outer parser sees structural characters in the wrong places.

Bonus: the meta-mistake

The mistake behind most of the above: treating URL encoding as a single concept rather than a set of context-specific rules.

URL encoding isn’t one thing. There’s strict RFC 3986 encoding (for paths), form-encoding (for query strings sent by browsers), OAuth-strict encoding (for signing), and a few others. Each has different rules about which characters get encoded and how spaces are handled.

The fix isn’t to memorize all the variants. The fix is to use the right standard library function for your context — and to let the URL/URLSearchParams APIs handle the details where possible. Manual encoding is where bugs hide.

Quick checklist before shipping URL-handling code

  1. Am I encoding the whole URL or just the values? (Should be just the values.)
  2. Could this value have come from somewhere already encoded? (If so, decode first or don’t re-encode.)
  3. Am I using encodeURIComponent, not encodeURI? (For values, yes.)
  4. Is the user input encoded? (Always, before concatenation.)
  5. Path or query? (Different encoding rules; reach for the right tool.)
  6. Am I using escape() anywhere? (Don’t.)
  7. Manual string replacement? (Use the standard library instead.)
  8. Plus signs in query strings? (Use URLSearchParams for decoding.)
  9. Encoded values in the database? (No — store raw.)
  10. Embedded URLs as parameters? (Encode the inner one fully.)

If you can answer all ten cleanly, your URL handling is in good shape.


Found this useful? Try the URL decoder, the URL encoder, or browse all tools.

More reading

From the blog.