Every character that needs percent-encoding in a URL, what it encodes to, and when you actually need to encode it. Bookmark this page as your reference for URL-safe vs URL-reserved characters.
The complete list of URL-reserved characters that must be percent-encoded when they appear inside URL data (i.e., not used for their structural meaning). All examples use UTF-8 byte values.
| Character | Name | Encoded | Where reserved |
|---|---|---|---|
| space | space | %20 | Anywhere |
| ! | Exclamation | %21 | Sub-delimiter |
| " | Double quote | %22 | Always unsafe |
| # | Hash / fragment | %23 | Separates fragment |
| $ | Dollar | %24 | Sub-delimiter |
| % | Percent | %25 | Always — starts an encoding |
| & | Ampersand | %26 | Separates query params |
| ' | Apostrophe | %27 | Sub-delimiter |
| ( | Left paren | %28 | Sub-delimiter |
| ) | Right paren | %29 | Sub-delimiter |
| * | Asterisk | %2A | Sub-delimiter |
| + | Plus | %2B | Means space in form-encoded data |
| , | Comma | %2C | Sub-delimiter |
| / | Forward slash | %2F | Path separator |
| : | Colon | %3A | Scheme delimiter |
| ; | Semicolon | %3B | Sub-delimiter |
| < | Less than | %3C | Always unsafe |
| = | Equals | %3D | Key/value separator |
| > | Greater than | %3E | Always unsafe |
| ? | Question | %3F | Starts query string |
| @ | At sign | %40 | Userinfo separator |
| [ | Left bracket | %5B | IPv6 hostname |
| \ | Backslash | %5C | Always unsafe |
| ] | Right bracket | %5D | IPv6 hostname |
| ^ | Caret | %5E | Always unsafe |
| ` | Backtick | %60 | Always unsafe |
| { | Left brace | %7B | Always unsafe |
| | | Pipe | %7C | Always unsafe |
| } | Right brace | %7D | Always unsafe |
RFC 3986 (the URI standard) splits all printable characters into three groups based on how URLs use them:
Unreserved. Letters A-Z, a-z, digits 0-9, and four marks: -, _, ., ~. These never need encoding and are always safe in a URL.
Reserved. The 18 characters listed above as gen-delims and sub-delims. They have a syntactic meaning in URLs — like ? starting a query or & separating params. You must encode them when they appear as data rather than structure.
Unsafe. Everything else: spaces, control characters, <, >, {, }, and so on. Must always be encoded.
Only when it appears as part of your data, not part of the URL’s structure. Three examples:
The & in ?q=hello&lang=en is structural — it separates two query parameters. Don’t encode it. But the & in ?company=Smith %26 Sons is data, so it must be encoded as %26 or the parser will see two parameters: company=Smith and Sons.
The / in /products/apparel is structural — it separates path segments. Don’t encode it. But the / in a product name like A/B Testing is data; it must be encoded as %2F when used as a path segment, or the server will think it’s two segments.
The ? in /search?q=test is structural — it introduces the query string. Don’t encode it. But the ? in a question like “What time?” passed as a value must be encoded as %3F or the parser thinks the query string ends there.
Anything outside ASCII — accented letters, Asian scripts, emoji, mathematical symbols — must be UTF-8 encoded to bytes first, then each byte percent-encoded. The letter é is two bytes in UTF-8 (0xC3 0xA9), so it becomes %C3%A9. The heart emoji ❤ is three bytes (0xE2 0x9D 0xA4), so it becomes %E2%9D%A4.
When you’re unsure, encode it. Over-encoding is always safe; under-encoding breaks things. Use encodeURIComponent (JavaScript), quote (Python), or rawurlencode (PHP) on any value that’s data going into a URL. These functions encode all reserved and unsafe characters — leaving only the unreserved 66 as-is.
Only the 66 unreserved characters: letters A-Z and a-z, digits 0-9, and the four marks - _ . ~. Everything else is either reserved (must be encoded when used as data) or unsafe (must always be encoded).
Tilde is unreserved per RFC 3986 — it has no special meaning in URL syntax. Hash starts the fragment identifier (the part after #), so it needs to be encoded as %23 when it appears as data inside a URL.
No — the dot is in the unreserved set and never needs encoding. Two exceptions: file path components . (current directory) and .. (parent directory) have semantic meaning that URLs preserve, but the dot character itself doesn't get percent-encoded.
Inside a query parameter value, both must be encoded — = as %3D, & as %26 — otherwise the URL parser will misinterpret them as key/value separators. The form-encoded standard (application/x-www-form-urlencoded) does this automatically.
No — both underscore and hyphen are in the unreserved set per RFC 3986. They never need encoding. This is why URLs in the wild often use hyphens or underscores as word separators in slugs.