Percent-Encoding Reference: Reserved & Unreserved Characters
What Is Percent-Encoding?
Percent-encoding is the mechanism defined in RFC 3986 for representing data within a URI. It replaces characters with a percent sign % followed by two uppercase hexadecimal digits representing the character's byte value.
Space → %20
& → %26
é (UTF-8) → %C3%A9
This reference covers every character you need to know about when building or parsing URLs.
Reserved Characters
Reserved characters have special meaning in URI syntax. When they appear as data rather than delimiters, they must be percent-encoded.
Complete Reserved Characters Table
| Char | Hex | Encoded | Role in URI Syntax |
|---|---|---|---|
: | 3A | %3A | Scheme separator (https:), port separator (:443) |
/ | 2F | %2F | Path separator (/api/users) |
? | 3F | %3F | Query string start (?q=search) |
# | 23 | %23 | Fragment identifier (#section) |
[ | 5B | %5B | IPv6 address delimiter |
] | 5D | %5D | IPv6 address delimiter |
@ | 40 | %40 | Authority separator (user@host) |
! | 21 | %21 | Sub-delim |
$ | 24 | %24 | Sub-delim |
& | 26 | %26 | Query parameter separator (a=1&b=2) |
' | 27 | %27 | Sub-delim |
( | 28 | %28 | Sub-delim |
) | 29 | %29 | Sub-delim |
* | 2A | %2A | Sub-delim |
+ | 2B | %2B | Sub-delim (also means space in form encoding) |
, | 2C | %2C | Sub-delim |
; | 3B | %3B | Sub-delim (param separator) |
= | 3D | %3D | Query parameter assignment (key=value) |
When to encode reserved characters
- As data in query strings:
?url=https%3A%2F%2Fexample.com - As data in path segments:
/search/hello%20world - When the character's delimiter role is not intended:
?q=AT%26T(encoding the&in "AT&T")
When NOT to encode reserved characters
/between path segments:/api/users/42— leave as-is?starting a query string:?search=hello— leave as-is=assigning query values:?page=1— leave as-is&separating parameters:?a=1&b=2— leave as-is
Unreserved Characters
Unreserved characters never need encoding and should not be encoded. Encoding them produces equivalent but unnecessarily long URLs.
| Category | Characters | Count |
|---|---|---|
| Uppercase letters | A B C ... Z | 26 |
| Lowercase letters | a b c ... z | 26 |
| Digits | 0 1 2 ... 9 | 10 |
| Hyphen | - | 1 |
| Period | . | 1 |
| Underscore | _ | 1 |
| Tilde | ~ | 1 |
Total: 66 unreserved characters
Why tilde (~) matters
Older specifications (RFC 2396) classified ~ as a reserved character. RFC 3986 moved it to unreserved. Some legacy systems still encode ~ as %7E, but this is unnecessary and should be avoided.
Common Encoded Values Quick Reference
The table below covers frequently encoded characters with practical context:
| Character | Encoded | Common Scenario |
|---|---|---|
| Space | %20 | Spaces in any URL component |
" | %22 | Quotes in HTML attribute URLs |
% | %25 | Literal percent sign (avoid double-encoding) |
< | %3C | Angle brackets in URL data |
> | %3E | Angle brackets in URL data |
\ | %5C | Backslash in Windows paths |
^ | %5E | Caret in data values |
` | %60 | Backtick in data values |
{ | %7B | Curly brace in JSON data |
| ` | ` | %7C |
} | %7D | Curly brace in JSON data |
| Tab | %09 | Tab character in form data |
| Newline | %0A | Line break in text |
ASCII Printable Characters Full Table
Every printable ASCII character and its encoding status:
| Dec | Char | Encoded | Status | Dec | Char | Encoded | Status |
|---|---|---|---|---|---|---|---|
| 32 | Space | %20 | Encode | 58 | : | %3A | Reserved |
| 33 | ! | %21 | Reserved | 59 | ; | %3B | Reserved |
| 34 | " | %22 | Encode | 60 | < | %3C | Encode |
| 35 | # | %23 | Reserved | 61 | = | %3D | Reserved |
| 36 | $ | %24 | Reserved | 62 | > | %3E | Encode |
| 37 | % | %25 | Encode | 63 | ? | %3F | Reserved |
| 38 | & | %26 | Reserved | 64 | @ | %40 | Reserved |
| 39 | ' | %27 | Reserved | 91 | [ | %5B | Reserved |
| 40 | ( | %28 | Reserved | 92 | \ | %5C | Encode |
| 41 | ) | %29 | Reserved | 93 | ] | %5D | Reserved |
| 42 | * | %2A | Reserved | 94 | ^ | %5E | Encode |
| 43 | + | %2B | Reserved | 95 | _ | — | Unreserved |
| 44 | , | %2C | Reserved | 96 | ` | %60 | Encode |
| 45 | - | — | Unreserved | 123 | { | %7B | Encode |
| 46 | . | — | Unreserved | 124 | | | %7C | Encode |
| 47 | / | %2F | Reserved | 125 | } | %7D | Encode |
| 48–57 | 0–9 | — | Unreserved | 126 | ~ | — | Unreserved |
Characters 65–90 (A–Z) and 97–122 (a–z) are all unreserved and omitted from this table for brevity.
Unicode and UTF-8 Encoding
Non-ASCII characters use UTF-8 encoding before percent-encoding. Each UTF-8 byte becomes a separate %XX sequence.
| Character | UTF-8 Bytes | Percent-Encoded |
|---|---|---|
é | C3 A9 | %C3%A9 |
© | C2 A9 | %C2%A9 |
€ | E2 82 AC | %E2%82%AC |
中 | E4 B8 AD | %E4%B8%AD |
🎉 | F0 9F 8E 89 | %F0%9F%8E%89 |
Practical example
// Encoding a URL with Unicode
const name = 'José García';
const encoded = encodeURIComponent(name);
// "Jos%C3%A9%20Garc%C3%ADa"
// The space could also be + in form encoding
const formEncoded = new URLSearchParams({ name }).toString();
// "name=Jos%C3%A9+Garc%C3%ADa"
4 Practical Examples
1. Passing a URL as a query parameter
const targetUrl = 'https://example.com/search?q=hello&lang=en';
const link = `/redirect?url=${encodeURIComponent(targetUrl)}`;
// "/redirect?url=https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%26lang%3Den"
2. Encoding JSON data in a URL
const filters = JSON.stringify({ category: 'A&B', price: '<100' });
const url = `/products?f=${encodeURIComponent(filters)}`;
// "/products?f=%7B%22category%22%3A%22A%26B%22%2C%22price%22%3A%22%3C100%22%7D"
3. Building a mailto link with special characters
const subject = 'Questions & Answers';
const body = 'What is 50% off?";
const mailto = `mailto:help@example.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
4. Avoiding double-encoding
// WRONG: Double-encoding produces %2520 instead of %20
const already = encodeURIComponent('hello world'); // "hello%20world"
const double = encodeURIComponent(already); // "hello%2520world"
// CORRECT: Check before encoding
function safeEncode(str) {
try {
return encodeURIComponent(decodeURIComponent(str));
} catch {
return encodeURIComponent(str);
}
}
Related Guides
Try It Yourself
Encode and decode any string instantly with our free URL Encoder/Decoder tool. Perfect for debugging URLs, testing API parameters, or learning percent-encoding hands-on.