URL Encoding: How to Handle Special Characters in URLs

Everything you need to know about percent-encoding, reserved characters, and building safe URLs.

🔗%️⃣🌐

TL;DR: URL encoding replaces special characters with %XX so they don't break URL structure. Use encodeURIComponent() for individual values, URLSearchParams for query strings, and never forget to encode user input.

Why Can't URLs Just Be Normal?

Have you ever tried to search for "fish & chips" on a website and noticed the URL suddenly fills up with weird %20 and %26 characters? That's URL encoding in action, and it exists for a really good reason.

URLs have a very specific grammar. Certain characters like ?, &, =, and # are part of that grammar — they're like punctuation marks that tell the browser where different parts of the URL begin and end. If your actual data happens to contain these characters, the browser gets confused about what's structure and what's content.

🤯 The browser when your search query contains an &

A Real Example That Breaks

// You want to search for "fish & chips"
https://example.com/search?q=fish & chips

// The browser interprets this as:
//   q = "fish "          (just "fish" with a space)
//   chips = ""           (a separate empty parameter!)

// The FIX: encode the special characters
https://example.com/search?q=fish%20%26%20chips
// Now the server correctly receives: q = "fish & chips"

URL encoding (also called percent-encoding) replaces each unsafe character with a % sign followed by two hex digits representing that character's byte value. A space becomes %20, an ampersand becomes %26, and so on.

Space → %20    & → %26    = → %3D
?     → %3F    # → %23    / → %2F
😄

Dev Joke: A developer's favorite breakfast? URL-encoded eggs: %F0%9F%A5%9A. Just kidding, but that IS the URL-encoded version of the egg emoji.

Which Characters Need Encoding?

Safe characters (never need encoding):

A-Z  a-z  0-9  -  _  .  ~

Reserved characters (must be encoded when used as data, not as URL structure):

:  /  ?  #  [  ]  @  !  $  &  '  (  )  *  +  ,  ;  =

JavaScript's Three Encoding Functions (and When to Use Each)

encodeURIComponent() — Your Go-To

Use this for encoding individual values — a query parameter, a path segment, anything that's a single "piece" of a URL:

encodeURIComponent("fish & chips")
// "fish%20%26%20chips"

encodeURIComponent("user@example.com")
// "user%40example.com"

encodeURI() — For Complete URLs

This encodes a full URL but keeps the structural characters (:, /, ?, &) intact. Use it only when you have a complete URL with non-ASCII characters:

encodeURI("https://example.com/日本語/page")
// "https://example.com/%E6%97%A5%E6%9C%AC%E8%AA%9E/page"
⚠️

The critical difference: encodeURI() does NOT encode &, =, or ?. So if your data contains those characters, encodeURI() will break your URL. When in doubt, always use encodeURIComponent() for individual values.

URLSearchParams — The Modern, Foolproof Way

Honestly? Just use this. It handles all the encoding automatically and you don't have to think about which function to use:

const params = new URLSearchParams();
params.set("q", "fish & chips");
params.set("category", "food/drink");
console.log(params.toString());
// "q=fish+%26+chips&category=food%2Fdrink"

// Building a complete URL
const url = new URL("https://example.com/search");
url.searchParams.set("q", "fish & chips");
console.log(url.toString());
// "https://example.com/search?q=fish+%26+chips"
🛡️ URLSearchParams: the safe way to build query strings

URL Encoding in Other Languages

Python

from urllib.parse import quote, quote_plus, urlencode

quote("fish & chips")           # "fish%20%26%20chips"
quote_plus("fish & chips")      # "fish+%26+chips"
urlencode({"q": "fish & chips"}) # "q=fish+%26+chips"

PHP

urlencode("fish & chips");     // "fish+%26+chips"
rawurlencode("fish & chips");  // "fish%20%26%20chips"

Three Mistakes Everyone Makes

1. Double Encoding

Encoding something that's already encoded. Now your %20 becomes %2520, and the server gets very confused:

const once = encodeURIComponent("hello world"); // "hello%20world"
const oops = encodeURIComponent(once);           // "hello%2520world" — BAD!

2. Forgetting to Encode User Input

This isn't just a bug — it's a security hole. Unencoded user input can break requests or enable injection attacks:

// If username is "alice&admin=true" — this is exploitable!
const bad = `/api/user?name=${username}`;

// Always encode
const safe = `/api/user?name=${encodeURIComponent(username)}`;

3. Using the Wrong Function

Simple rule: encodeURIComponent() for values, encodeURI() for full URLs, URLSearchParams for query strings.

💡

Fun fact: The + sign for spaces (like fish+chips) is actually a different standard — it comes from HTML form encoding (application/x-www-form-urlencoded). In regular URL paths, spaces must be %20. Both are valid in query strings, but only %20 is correct in paths.

Unicode Characters in URLs

When you put non-ASCII characters in a URL (Chinese, Arabic, emoji), they first get converted to their UTF-8 byte sequence, then each byte gets percent-encoded:

encodeURIComponent("cafe\u0301")  // "caf%C3%A9" (2 UTF-8 bytes)
encodeURIComponent("🚀")     // "%F0%9F%9A%80" (4 UTF-8 bytes)

Try It Yourself

Encode or decode URLs and query parameters instantly. Paste any text to see its percent-encoded form, or decode a URL to reveal the original characters.

Open URL Encoder/Decoder →