URL Encoding Special Characters: The Full Reserved and Unreserved Sets
RFC 3986 sorts every character a URL might contain into three groups, and knowing the groups explains why some punctuation needs escaping and some does not. Unreserved characters, letters, digits, and the four marks - _ . ~, are always safe and never require encoding.1 Reserved characters, the gen-delims : / ? # [ ] @ and the sub-delims ! $ & ' ( ) * + , ; =, carry structural meaning and must be encoded whenever they appear as literal data rather than as delimiters.2
Every other character, including space, quotation marks, angle brackets, and all non-ASCII text, falls outside both sets and must always be percent-encoded. Consequently, the question "does this character need escaping" always has the same answer: check which of the three groups it belongs to. The CapyToolkit encoder applies this classification automatically, so pasting any string reveals exactly which characters it altered.
The unreserved set that never needs escaping
Four characters have permanent free passage through a URL alongside letters and digits. RFC 3986 Section 2.3 names them explicitly: hyphen, underscore, period, and tilde, joined by the alphanumeric range A-Z, a-z, and 0-9. These four marks are not leftovers or conventions; they are the exact set the specification guarantees will never need percent-encoding in any position of a URL, from the first path segment through the final fragment.
Why these four marks stay stable everywhere
These characters are guaranteed to mean the same thing in every context of a URL, path, query, or fragment, because no delimiter in the grammar uses them. Consequently, an encoder never touches them regardless of position, and encodeURIComponent leaves all of them alone even though it escapes nearly everything else.3 This is also why file names, slugs, and identifiers built from letters, digits, and hyphens travel through URLs without any encoding step at all, a property web developers lean on constantly when designing readable paths. This stability is also why RFC 3986 calls the four marks out by name rather than leaving them open to interpretation, since a URI producer and a URI consumer that disagreed on even one of them would break compatibility across the entire web.
Gen-delims and sub-delims: reserved but conditional
Reserved characters occupy a middle ground: safe as structure, unsafe as data.4 RFC 3986 splits them into gen-delims, which separate the major components of a URI (: / ? # [ ] @), and sub-delims, which have meaning within a component (! $ & ' ( ) * + , ; =). The practical consequence is that whether a reserved character needs encoding depends on the role it plays in a specific URI component rather than on its identity alone.
Encoding depends on position, not identity
Understanding the colon exception right after a URL scheme explains why https: never needs escaping, even though that same colon turns dangerous the moment it shows up as literal data inside a path segment. An ampersand follows the identical logic: fine as a query delimiter, but corrosive to a value if left raw inside one.
This is why encodeURIComponent escapes every reserved character: it assumes you are handing it a single component where none of these characters should carry structural meaning.5 encodeURI, by contrast, trusts that you have already placed them correctly and leaves them alone. The correct escaping of a reserved character is therefore a question of role, not of the character itself. Recognizing which role a character plays in a given URI, delimiter or data, is the real skill behind correct encoding, more so than memorizing the character list.
A common mistake is to assume that because a character is safe in one position it is safe everywhere. A query parameter value containing & must encode that ampersand as %26, or the server will treat it as a delimiter and split the value into a new parameter. The same & between two parameters is correct as a literal delimiter and should never be encoded there. Both situations involve the same character, but only one of them requires escaping, which is why context is what determines correctness.
Everything else must always be encoded
Characters outside both the unreserved and reserved sets have no legal unencoded form anywhere in a URL2, which means every instance of these characters must be escaped before the string travels. This group includes the space, quotation marks, angle brackets, backtick, curly braces, pipe, backslash, and caret, along with the entire range of non-ASCII text such as accented letters, CJK characters, and emoji.
Furthermore, this is the largest and least memorable group, so the practical approach is not to memorize it but to let an encoder classify each character for you. encodeURIComponent and encodeURI both escape this entire category identically, since neither function ever treats these characters as safe. The only decision that changes between the two functions is how they treat the reserved set; the unreserved set and the always-encode set are handled the same way by both, which narrows the real decision to a much smaller question. Even punctuation that looks harmless, such as a caret or a backtick, has no defined meaning anywhere in the URI grammar, so a decoder cannot assume anything about it beyond the byte value the percent-escape spells out.
Why non-ASCII characters always encode
The same universal-escape rule applies to every non-ASCII character regardless of whether it looks like punctuation or prose. Because percent-encoding operates on UTF-8 bytes, a single accented letter can produce two or more escapes, and an emoji can produce four, but in every case the result is safe to include in any component of a URL. There are no exceptions based on language or script: if a character is outside the unreserved or reserved sets, both encodeURIComponent and encodeURI escape it in full.
When to use this
Consult this classification when you need to predict whether a character will survive encoding unchanged, or when auditing a URL-building function for correctness. Paste a string containing the character in question into CapyToolkit's ENCODE mode and compare the encodeURIComponent and encodeURI rows: an unreserved character stays identical in both, a reserved character differs between them, and everything else is escaped in both. This three-way split is a fast way to settle an argument about whether a given character "needs" encoding, since the answer is rarely a matter of opinion once you check which of the three groups it falls into.
Examples
An unreserved slug
my-file_v2.final~draft
my-file_v2.final~draft
Letters, digits, hyphen, underscore, period, and tilde pass through both encoders unchanged.
A reserved character as data
price:19.99
price%3A19.99
encodeURIComponent escapes the colon because it treats it as data, not as a URI scheme separator.
A reserved character as structure
https://example.com/x
https://example.com/x
encodeURI leaves the colon and slashes intact because they are genuine structural delimiters here.
An always-encoded character
quote "test"
quote%20%22test%22
The quotation marks and space fall outside both the unreserved and reserved sets, so both encoders escape them.
- 1.
IETF, "Unreserved Characters," RFC 3986 Section 2.3, rfc-editor.org, January 2005. https://www.rfc-editor.org/rfc/rfc3986.html#section-2.3
- 2.
IETF, "Reserved Characters," RFC 3986 Section 2.2, datatracker.ietf.org, January 2005. https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
- 3.
Mozilla Developer Network, "encodeURIComponent()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
- 4.
WHATWG, "URL Standard," url.spec.whatwg.org, accessed July 2026. https://url.spec.whatwg.org/
- 5.
ECMA, "encodeURIComponent (uriComponent)," ECMAScript 2027 Language Specification, Section 18.2.6.1, tc39.es, July 2026. https://tc39.es/ecma262/#sec-encodeuricomponent-uricomponent