YamlBox

Web & encoding · 5 min read

URL encoding: when to escape, and when you are breaking things

URLs are assembled from characters that mean something to the parser and characters that are simply data, and percent-encoding is how the two are kept apart. A URL encoder converts the latter into `%XX` escapes so a value containing `&`, `?` or a space survives the trip. Most URL bugs are not encoding failures at all — they are encoding the wrong part, or encoding twice.

Updated

What must be encoded

A small set of characters is always safe — letters, digits, `-`, `_`, `.` and `~`. Everything else is either reserved or should be escaped. Reserved characters are the interesting ones, because they are legal in a URL but only in specific positions: `?` starts a query, `&` separates parameters, `=` assigns a value, `#` starts a fragment, `/` separates path segments. Put one of those inside a value unencoded and you have silently rewritten the URL’s structure.

Plus or %20

Space is the classic example. In a path, a space must be `%20`; a `+` there is a literal plus. In `application/x-www-form-urlencoded` bodies and query strings read by form parsers, `+` is understood as a space. That divergence is why a value can encode to something that works in one context and breaks in another, and why decoders offer both interpretations.

Encode components, not whole URLs. Running a complete URL through a component encoder escapes its own `://` and `?`, producing a link that resolves to nothing.

The JavaScript distinction that catches everyone

                encodeURI('https://x.com/a b?q=1')        // keeps :// ? = — for a whole URL
encodeURIComponent('a b?q=1')               // escapes ? and = — for one value
              

`encodeURI` preserves URL structure and is meant for a complete address; `encodeURIComponent` escapes everything significant and is meant for a single parameter value. Using the wrong one produces either a broken URL or an unescaped value, and the symptoms are opposite: a link that 404s, or a query string that loses half its parameters.

Double encoding and signed URLs

Encoding an already-encoded value turns `%20` into `%2520`, and the server then decodes it once and hands your application a literal `%20` where a space was intended. This shows up in layered systems — a proxy re-encoding a target, a client escaping a URL it received already escaped. It matters most for signed URLs: signatures are computed over the exact encoded string, so changing one character’s escaping invalidates the link even though it still resolves to the same resource.

Decode to debug

When a link is failing, decoding it is the fastest way to see what the server is actually being asked for — and the answer is often a value that leaked into the structure. Working in the browser means you can paste a URL containing tokens, signed parameters or customer identifiers without sending it anywhere, which is precisely the kind of URL you should not paste into a service that logs requests.

URL Encoder / Decoder — frequently asked questions

Should a space be %20 or a plus sign?

Use `%20` in paths and in any URL you construct yourself; both are valid in query strings, where form parsers conventionally read `+` as a space. Decoding needs to know which convention produced the string.

Why does encoding my URL twice break it?

Each pass escapes the escape character: a space becomes `%20`, then `%2520`. On arrival the server decodes once and your application receives literal `%20` text. Encode once, at the point where a raw value becomes part of a URL.

What is the difference between encodeURI and encodeURIComponent?

`encodeURI` leaves structural characters such as `://`, `?`, `&` and `=` intact, so it suits a whole address. `encodeURIComponent` escapes those as well, so it suits a single parameter value. Mixing them up is the cause of most broken generated links.

Tools mentioned in this post

Browse all 19 tools →

Keep reading

All 19 posts →