All operations

URL encoding

Percent-escape the characters a URL cannot carry as themselves.

What it does

Percent-encoding replaces a character with % and the two hex digits of each byte in its UTF-8 form. A space becomes %20. An accented letter takes two escapes, so naïve encodes to na%C3%AFve.

Encoding follows JavaScript’s encodeURIComponent, which treats the input as one component value and escapes the structural characters with the rest. Paste a whole address in and %3A%2F%2F appears where the scheme separator was. Correct for a query parameter. Wrong for an address bar.

Reserved and unreserved

RFC 3986 lets a small set stand for itself: the letters, the digits, hyphen, period, underscore and tilde. A second set is reserved for structure, / between path segments and ? before the query among them. A character in neither set, a space or an angle bracket, has no legal raw form in a URI.

Five reserved characters come through untouched here: !, ', (, ) and *. All five counted as unreserved under the older RFC 2396, and encodeURIComponent still follows that list. Escape them yourself if the consumer treats them as delimiters.

Decoding twice

The example above is a double-encoded traversal with two decode stages on it. Skip the second stage and the output still reads %2e%2e%2f%2e%2e%2fetc%2fpasswd. Everything the attack depends on lives in that second pass.

A request meets more than one decoder: the web server resolves the path before routing, a proxy may decode before matching its rules, application code may decode a parameter again. %2e%2e%2f is ../, so a filter searching the raw path for ../ finds nothing. Encode the percent signs too and the filter’s own decoding yields text that passes its check, while the layer beneath turns it into a traversal.

Decode once, at a point you can name, then validate the result. If nothing legitimate still carries a % by then, reject what does.

Escaping the letters too

The Encode every character switch escapes everything, letters included: /etc/passwd becomes %2F%65%74%63%2F%70%61%73%73%77%64. Every server resolves that to the same path; signature matching against the raw string does not. It shows whether a filter normalizes before inspecting.

Plus signs

Form bodies encode a space as +. That rule belongs to application/x-www-form-urlencoded and never applied to a URL path. This operation writes %20 and, decoding, leaves + alone, so a captured POST body keeps its plus signs where its spaces were. Convert them first if that is what you are reading.

HTML entities escapes for a different sink and breaks the same way once two decoders sit in a row. Punycode covers the one part of a URL percent-encoding never touches, the hostname. Hexadecimal reads the bytes behind each escape.