URL Encoding Explained: Percent-Encoding, encodeURI vs encodeURIComponent, and the %2520 Bug

July 11, 2026 · 7 min read

Every developer who has built a URL with user input has hit this: a search term with a space or an ampersand breaks the link, and suddenly half the query is missing on the server. The fix is URL encoding, also called percent-encoding — but it comes with two functions that are easy to confuse and one bug that produces the baffling %2520 in your links. This guide clears all of it up.

Why URLs need encoding at all

URLs are only allowed to contain a limited set of characters, defined by a standard called RFC 3986. That set is basically the unreserved characters — letters, digits, and a few symbols like hyphen and underscore — plus a handful of reserved characters that have special structural meaning, such as ? to start a query, & to separate parameters, = to pair a key with a value, / for path segments, and # for a fragment. Anything outside that set — spaces, accented letters, emoji, or a reserved symbol used as data rather than structure — has to be encoded, or the URL breaks.

Percent-encoding does this by replacing an unsafe character with a percent sign followed by its two-digit hexadecimal byte value. A space becomes %20. An ampersand becomes %26. An at-sign becomes %40. On the other end, the server decodes these back to the original characters. The whole system exists so that arbitrary text can ride inside a URL's rigid structure without being mistaken for part of that structure.

The core problem: data vs structure

Here is the situation that causes most bugs. Imagine a search for the literal text "cats & dogs". If you drop that straight into a query string as q=cats & dogs, the & is read as a parameter separator — the server sees a parameter q=cats and a second meaningless parameter dogs, and your search is broken. The space causes trouble too. The ampersand needs to become %26 and the space %20 so the server understands they are part of the value, not URL structure. Encoding is how you say "this character is data, not a delimiter."

encodeURI vs encodeURIComponent

JavaScript gives you two encoding functions, and choosing the wrong one is one of the most common URL bugs. The difference is what they consider "safe" and leave alone.

encodeURIComponent encodes everything reserved, including / ? & = # and more. It is meant for encoding a single value that you are inserting into a URL — a query parameter value, a search term, a redirect target. Use it for the pieces. encodeURI, by contrast, deliberately leaves the URL structure characters (/ ? & =) alone, because it is meant to encode an entire, already-assembled URL while keeping it functional. Use it for the whole thing, rarely.

The rule of thumb: if you are encoding a value that goes inside a URL, use encodeURIComponent. If you are encoding a complete URL and only want to fix stray illegal characters without breaking its structure, use encodeURI. When in doubt, you almost always want encodeURIComponent, because you are almost always encoding a value.

Wrong choice example: using encodeURI on a redirect parameter leaves the & and = in the redirect's own query string unencoded, so the outer URL misreads them. encodeURIComponent on that value encodes them to %26 and %3D, keeping the value intact.

The %2520 bug: double-encoding

This one confuses everyone the first time. You see %2520 in a URL where you expected %20, and the link is broken. Here is what happened: a string that was already encoded got encoded a second time. The first encoding turned a space into %20. The second encoding then encoded the percent sign itself — % becomes %25 — turning %20 into %2520. Now the server decodes it once to %20 (literally the text "%20") instead of a space, and everything downstream is wrong.

The cause is almost always encoding a value that was already encoded — for example, building a URL from a string that already came URL-encoded, then running it through the encoder again. The rule to avoid it: only ever encode raw, un-encoded text. Never encode a URL or value that is already encoded. If you are not sure whether a string is already encoded, decode it first and then encode it exactly once.

The plus sign ambiguity

One more trap. In a query string, a plus sign (+) is sometimes interpreted as a space — a legacy of the older HTML form encoding, application/x-www-form-urlencoded. But in a URL path, + is a literal plus. This inconsistency means a + in your data can be read as a space in one place and a plus in another. The safe move is to always encode a space as %20, which is unambiguous everywhere, rather than relying on +. If you see spaces turning into pluses or vice versa, this ambiguity is usually the culprit.

The practical takeaway

Encode the values you put into URLs, use encodeURIComponent for those values, encode only raw text to avoid the %2520 double-encoding bug, and prefer %20 over + for spaces. When you need to build or inspect a URL by hand, a quick encoder and decoder lets you see exactly what a string becomes and catch these issues before they ship.

The TextCaret URL Encode / Decode tool percent-encodes text the way encodeURIComponent does, so you can safely prepare a query value or decode a mysterious %2520 to see what went wrong — all in your browser.