Every URL you have ever shared has been percent-encoded, even if you never saw it. The space in “hello world” becomes hello%20world; the em dash in a Chinese-language URL becomes %E2%80%94; the search query for “café” becomes caf%C3%A9. Understanding what these percent signs mean — and when to encode them yourself — is the difference between a URL that works and one that mysteriously 404s.

What Percent-Encoding Is

URLs were designed in 1994 to carry only a small set of characters: the ASCII letters A–Z, a–z, the digits 0–9, and a handful of safe symbols. The full grammar is defined in RFC 3986, which calls this set the “unreserved” characters plus the “reserved” characters that have specific structural meaning in a URL (:, /, ?, #, [, ], @, !, $, &, ', (, ), *, +, ,, ;, =).

Anything else must be percent-encoded: each byte of the input becomes a percent sign followed by two uppercase hex digits. space (0x20) becomes %20; the German ä (U+00E4) becomes %C3%A4 because its UTF-8 encoding is the two bytes 0xC3 and 0xA4. The browser then knows that on the receiving side, the bytes C3 A4 should be reassembled into the single character U+00E4.

The encoding is reversible: decodeURIComponent('%C3%A4') in JavaScript returns ä. The encoding is lossy on length: a single character may expand into 9, 12, or even 18 percent-encoded characters (for emojis outside the BMP). For typical query strings, the size overhead is around 30–50%.

The Two JavaScript Encoders

JavaScript exposes two encoders, and the difference between them is the most common source of URL encoding bugs.

encodeURIComponent encodes everything except the unreserved set: A-Z a-z 0-9 - _ . ! ~ * ' ( ). That is the strict encoder. It treats every other character — including the URL-reserved characters like /, ?, and & — as data that needs to be escaped. The standard use case is encoding a single query parameter value or path segment that will be embedded into a larger URL.

encodeURI encodes everything except the unreserved set plus the URL-reserved characters: A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #. It is the lenient encoder. It treats the entire URL as a structured template and only escapes the characters that have no structural meaning or that are unsafe to transmit.

The most common mistake is encoding a full URL with encodeURIComponent:

// WRONG: turns the URL into a single opaque string
encodeURIComponent('https://example.com/search?q=hi');
// => "https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhi"

// RIGHT: preserves the structure
encodeURI('https://example.com/search?q=hi');
// => "https://example.com/search?q=hi"

// ALSO RIGHT: encode each component separately
'https://example.com/search?q=' + encodeURIComponent('hi');
// => "https://example.com/search?q=hi"

The first version is “right” in that it produces a valid (if unusable) percent-encoded string; the receiving server would see the entire URL as a single hostile blob that does not match any route. The second and third versions both produce the same parseable URL. The URL Encoder exposes both modes so you can see the difference side-by-side.

How Unicode Travels Through URLs

Unicode characters in a URL are a two-step process. First, the character is encoded as a sequence of bytes using UTF-8 (the standard web encoding). Then each byte is percent-encoded. The em dash (U+2014) is three bytes in UTF-8 (0xE2 0x80 0x94) and becomes %E2%80%94 in the URL. The bomb emoji 💣 (U+1F4A3) is four bytes in UTF-8 (0xF0 0x9F 0x92 0xA3) and becomes %F0%9F%92%A3 — a 12-character encoding for a single character.

Browsers and servers handle this transparently. When you paste a URL like https://example.com/café into the address bar, the browser converts it to https://example.com/caf%C3%A9 before sending the request. The server then decodes the path back to café for the application code. Most application codebases never need to think about this; modern web frameworks handle encoding and decoding at the framework boundary.

Form Encoding: The Sibling of Percent-Encoding

HTML form submissions use a slightly different encoding, application/x-www-form-urlencoded, where the space is encoded as + instead of %20 and parameters are joined with &:

q=hello+world&lang=en&page=1

This is the format produced by HTML form submissions with method="GET" or method="POST" and content type application/x-www-form-urlencoded. The JavaScript URLSearchParams API handles this encoding correctly:

const params = new URLSearchParams();
params.append('q', 'hello world');
params.append('lang', 'en');
params.toString();
// => "q=hello+world&lang=en"

A common bug: building a URL by concatenating strings and forgetting to encode values that contain special characters. The string 'https://example.com/search?q=' + userInput is unsafe because userInput could contain &, =, #, or other characters that change the URL's meaning. The right pattern is to encode each component before joining:

const url = 'https://example.com/search?' + new URLSearchParams({ q: userInput }).toString();

When You Need to Encode Manually

Most web frameworks handle URL encoding at the boundary. You rarely need to call encodeURIComponent yourself. The exceptions:

  • Building URLs from raw user input — search boxes, form fields, anything where the user can paste a URL fragment. Always encode the user-supplied portion before concatenating.
  • Sharing non-ASCII URLs in plain text — some chat and email clients mangle non-ASCII characters. Percent-encoding makes the URL byte-safe and portable.
  • Debugging a broken URL — when a URL does not behave as expected, decode it and look for unintended encoding (a stray %2F in a path, a double-encoded %2520).
  • Generating links in static site generation — markdown processors and static site builders that produce HTML sometimes need explicit guidance on encoding.

Common Pitfalls

Real-world URL encoding bugs fall into a few recurring categories:

  • Encoding a full URL with encodeURIComponent instead of encodeURI. The result is a syntactically valid URL that points to a non-existent resource because the structural characters are escaped.
  • Forgetting to encode user input. Concatenating user input into a URL string without encoding: https://example.com/search?q=' + input — if input contains &, the URL is malformed.
  • Double encoding. Encoding an already-encoded URL produces percent signs inside percent signs: hello%20world becomes hello%2520world. Most servers decode once and then leave the result alone; double-encoded URLs appear broken until decoded twice.
  • Encoding the wrong direction. Some servers want form-encoded input (+ for spaces); others want percent-encoded (%20). The standard for URLs is percent-encoding; the standard for form bodies is form-encoding with +. Mixing them up produces a payload that does not parse correctly.

Verifying Encoding in Practice

Open your browser's developer tools and inspect a network request. The browser-built URL (visible in the address bar) is the decoded version; the URL actually sent (visible in the network tab) is the percent-encoded version. Comparing the two is the fastest way to see what your server is actually receiving. The URL Encoder can also be used to reverse-engineer a URL: paste the encoded form and read the decoded version to see what the application code will see.

The Bottom Line

Percent-encoding is the wire-level representation of URLs. The browser and server handle it transparently in the normal flow, but every developer eventually needs to know what the percent signs mean: how to encode a value, how to decode a string, and how to debug a URL that mysteriously does not work. Use encodeURIComponent for query parameter values and path segments; use encodeURI for full URLs you want to preserve the structure of; use URLSearchParams for form-style data. When in doubt, decode the URL with the URL Encoder and look at the result.

Further Reading

  • RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax, the formal specification of the URL grammar and the unreserved/reserved character sets.
  • RFC 1866 — the HTML 2.0 specification, which introduced the application/x-www-form-urlencoded content type.
  • WHATWG URL standard — the modern browser-side definition of how URLs are parsed and serialized.
  • MDN: encodeURIComponent — the canonical reference for the JavaScript encoder, with examples.

Frequently Asked Questions

Should I encode the & in a query parameter? Use encodeURIComponent on the value, which will encode the ampersand. If you then concatenate the value into a URL, the & separator stays structurally meaningful and the value’s %26 does not break the URL.

Why is there a + in some URLs instead of %20? HTML form submissions use application/x-www-form-urlencoded, which encodes spaces as + instead of %20. Both forms decode to the same value. The URL bar typically shows the + form for form-encoded URLs and the %20 form for plain percent-encoded URLs.

Is there a difference between uppercase and lowercase percent encoding? Functionally no. RFC 3986 recommends uppercase hex digits as a convention, but parsers are required to accept both. The URL Encoder emits uppercase to match the convention.

Do I need to encode UTF-8 manually before percent-encoding? No. The JavaScript encoders handle UTF-8 internally. encodeURIComponent('café') produces caf%C3%A9 directly, with the UTF-8 encoding handled by the language runtime.