What Percent-Encoding Does
URLs may only contain a limited set of ASCII characters. Anything outside that set — spaces, accented letters, most punctuation — must be replaced by a % followed by the character's byte value in hexadecimal. A space becomes %20, and é becomes %C3%A9 because it is two bytes in UTF-8.
The rules come from RFC 3986, which divides characters into three groups.
The Three Character Classes
| Class | Characters | Encoded? |
|---|---|---|
| Unreserved | A–Z a–z 0–9 - . _ ~ | Never |
| Reserved | : / ? # [ ] @ ! $ & ' ( ) * + , ; = | Only when used as data rather than structure |
| Everything else | Spaces, accents, most symbols | Always |
The reserved characters are the crux. In ?name=value&other=2 the ?, = and & are structural. If a value itself contains an ampersand, it must be encoded as %26 or it will be read as the start of the next parameter.
Component or Full URL
This distinction causes most real-world bugs.
| Component encoding | Full URL encoding | |
|---|---|---|
| JavaScript function | encodeURIComponent() | encodeURI() |
| Escapes & = ? / # | Yes | No |
| Use for | A single query value or path segment | An entire URL that is already structured |
Encoding a whole URL with component encoding destroys it — https:// becomes https%3A%2F%2F. Encoding a query value with full-URL encoding leaves dangerous characters intact. Choose by what the string is, not by how it looks.
Common Escape Sequences
| Character | Encoded | Why it matters |
|---|---|---|
| space | %20 | Also written as + in query strings |
| ! | %21 | |
| # | %23 | Otherwise starts the fragment |
| $ | %24 | |
| % | %25 | Otherwise begins an escape sequence |
| & | %26 | Otherwise separates parameters |
| + | %2B | Otherwise read as a space |
| / | %2F | Otherwise a path separator |
| : | %3A | |
| = | %3D | Otherwise separates key and value |
| ? | %3F | Otherwise starts the query string |
| @ | %40 |
Space: %20 or +?
Both appear, and they come from different specifications. %20 is correct percent-encoding and is valid anywhere in a URL. The + convention comes from HTML form submission (application/x-www-form-urlencoded) and is valid only in the query string.
A plus sign in a path is a literal plus; in a query string it usually means a space. This is why email addresses containing a plus — name+tag@example.com — break so often in web forms.
Non-ASCII Characters
Percent-encoding operates on bytes, so the character must first be encoded as UTF-8. Multi-byte characters therefore produce multiple escape sequences:
| Character | UTF-8 bytes | Encoded |
|---|---|---|
| é | C3 A9 | %C3%A9 |
| ü | C3 BC | %C3%BC |
| ç | C3 A7 | %C3%A7 |
| 中 | E4 B8 AD | %E4%B8%AD |
| 😀 | F0 9F 98 80 | %F0%9F%98%80 |
Internationalised domain names work differently — the host portion uses Punycode rather than percent-encoding, so bücher.de becomes xn--bcher-kva.de.
Security Notes
- Never decode twice. Double-decoding is a classic path traversal vector:
%252e%252e%252fdecodes once to%2e%2e%2fand again to../. - Encode on output, not input. Store the raw value and encode it when building each URL, or you will end up with double-encoded data.
- Percent-encoding is not sanitisation. It makes a string safe for a URL, not safe for HTML, SQL or a shell.
Frequently Asked Questions
Why does my URL show %20 everywhere?
Those are spaces. Browsers usually display them as spaces in the address bar but send them encoded.
Which mode should I use?
Component for a single value you are inserting into a URL; full URL for a complete address you want to make safe without breaking its structure.
Do I need to encode the whole URL?
No, and doing so breaks it. Encode only the parts that carry data — query values and path segments containing user input.
Is my text uploaded anywhere?
No. The conversion uses your browser's built-in functions. There is no network request.