URL Encoding Special Characters: The Complete Guide

·8 min read

Every developer eventually gets burned by URL encoding. A user searches for "cats & dogs", your redirect turns into a broken link, and a callback URL silently drops half its query string. This guide covers what percent-encoding actually is, which characters need it, and the correct JavaScript function for each situation.

What is percent-encoding?

URLs are limited to a small subset of ASCII. Anything outside that set — spaces, non-Latin letters, punctuation with special meaning — has to be replaced with a % followed by the character's UTF-8 byte in hex. A space becomes %20, an ampersand becomes %26, and an emoji like 🎉 becomes %F0%9F%8E%89 (four bytes, four percent-hex pairs).

The rules come from RFC 3986. Two categories matter:

  • Reserved characters: / ? # [ ] @ ! $ & ' ( ) * + , ; = — have syntactic meaning inside URLs. Encode them when they appear in data, leave them alone when they're structural.
  • Unreserved characters — letters, digits, and - _ . ~ — never need encoding.

encodeURI vs encodeURIComponent — the one that trips everyone up

JavaScript ships two encoders and they do different jobs:

  • encodeURI(url) — encodes a whole URL and leaves reserved characters alone. Use it when you have a URL that just has spaces or non-ASCII in it.
  • encodeURIComponent(value) — encodes a single value that will be inserted into a URL. It also encodes & = ? #, so it's safe for query-string values.
const query = "cats & dogs";
const badUrl = "https://example.com/search?q=" + query;
// → https://example.com/search?q=cats & dogs  ❌ breaks the query

const goodUrl = "https://example.com/search?q=" + encodeURIComponent(query);
// → https://example.com/search?q=cats%20%26%20dogs  ✅

The +, space, and application/x-www-form-urlencoded gotcha

HTML forms encode spaces as +, not %20. That's a legacy of the pre-URL world. The two are equivalent inside query strings but not inside a path. Confusion arises when a value comes in with a literal + character:

const email = "alice+work@example.com";
const params = new URLSearchParams({ email });
console.log(params.toString());
// → email=alice%2Bwork%40example.com  ✅

// vs
const bad = "email=" + encodeURIComponent(email);
// Also correct — encodeURIComponent encodes + as %2B.

Use URLSearchParams whenever you're building query strings — it handles the + versus %20 difference correctly and joins everything with &.

When to encode paths vs query vs fragment

  • Path segments — encode / if it's part of the value (a filename with a slash), leave it if it separates segments. Use encodeURIComponent for each segment and join with /.
  • Query parameters — always encodeURIComponent both key and value. Or better, use URLSearchParams.
  • Fragment (#...) — encode spaces and reserved characters. encodeURIComponent is safe.

Common characters and their encodings

CharacterEncodedWhy
space%20 or +Not allowed literally.
&%26Separates query params.
=%3DSplits key from value.
#%23Starts the fragment.
?%3FStarts the query string.
+%2BMeans space in form-encoding.
/%2FSeparates path segments.
é%C3%A9Non-ASCII → UTF-8 bytes.

Test any string right now

Paste any URL or value into the URL Encoder & Decoder to see the encoded and decoded versions side by side, plus a breakdown of the parsed URL parts. Runs entirely in your browser.

Keep reading