URL Encoding Reference

URL percent-encoding reference: reserved characters mapped to their %XX encodings, with notes for quick lookup when building query strings and paths.

Practical Notes

  • encodeURI and encodeURIComponent are not interchangeableencodeURI keeps structural characters such as : / ? # & = and is meant for a whole URL; encodeURIComponent escapes them all and is meant for a single parameter value. Passing a full URL to encodeURIComponent turns https:// into https%3A%2F%2F and breaks it.
  • A space is + in a query string but %20 in a pathapplication/x-www-form-urlencoded encodes a space as +, while in a URI path + is a literal plus sign. Without distinguishing the two, searching for "a+b" and "a b" yields the same result. Decide whether to decode + based on where the value came from.
  • Percent-encode non-ASCII as UTF-8, never GBKThe same Chinese character produces completely different percent sequences under UTF-8 and GBK (中 is %E4%B8%AD, not %D6%D0). Both sides must agree on UTF-8 or the server will decode mojibake.

Data Sources

  • RFC 3986 (URI generic syntax)
  • WHATWG URL Standard
  • RFC 1866 (application/x-www-form-urlencoded)

Codes, names and definitions above come from public specifications; we claim no originality over the specification data itself. The arrangement, Chinese annotations and practical notes on this page are written by us.

How It Works

Percent-encoding is the URL-safe representation mandated by RFC 3986. A URL may contain only a small set of "unreserved" characters (letters, digits and - _ . ~) literally; everything else — reserved characters used outside their delimiter role, spaces, and any non-ASCII character — must be escaped so the parser does not mistake it for structure.

Rule: encode the character as UTF-8 bytes, then write each byte as %XX (XX = two uppercase hex digits). For example the Chinese character 中 is UTF-8 E4 B8 AD, encoded as %E4%B8%AD. Reserved chars (like ? # / & =) are only encoded when not used as delimiters — that is why ? separates the query but becomes %3F inside a value.

When to Use This Table

This table is for deciding whether a character should be percent-encoded. The underlying question is simple: URLs have two classes of characters — those that may appear literally in a path, and those that must be encoded. This table gives the encoded form and typical use of common characters.

When not to consult it: you are encoding whole documents, or handling non-ASCII text (Chinese, Japanese, emoji). This table covers common ASCII symbols; multi-byte characters are encoded as their UTF-8 bytes turned into %XX one by one — the mechanism is the same but the rules differ.

Rule of thumb: judge by where the character sits in the URL. In a query string &, = and + carry meaning (separator, key/value, space); in a path /, : and @ are structural; everything after # is the fragment. Characters with meaning at their position must be encoded.

Mind the space character: in a query string it may be written as +, but in a path it must be %20. This is the easiest place to get it wrong — using + for space is invalid in a URL path, and the server will treat it as a literal plus sign.

Boundaries and Common Pitfalls

  • Spaces are written differently in query strings and paths

    a=b+c and a=b%20 are equivalent inside an application/x-www-form-urlencoded query string, because that specification defines + as space. Paths have no such rule: %20 is the only correct form. HTML form submission (default enctype) naturally produces +, while a space in a REST path must be written as %20 explicitly or some servers will treat it as a literal plus.

  • Double encoding is a common source of bugs

    Encoding an already-encoded string twice turns %20 into %2520, because the percent sign itself gets encoded to %25. Typical triggers: the frontend calls encodeURIComponent and then a backend or gateway encodes again, or an already-encoded URL is passed in as the "raw URL". Detection: a valid encoded result never contains %25 — if you see it, something was encoded twice. The fix is to agree on which layer performs encoding and have every other layer pass the value through untouched.

  • Some characters are auto-decoded by the browser, which misleads debugging

    Modern browsers normalise characters such as %2F (slash) and %3A (colon) in the address bar, so what you see in the Network panel is decoded while your server logs may hold the raw value — the mismatch makes correct encoding look broken. When debugging encoding issues, rely on the original URL (the Request URL rather than the displayed one) and check whether the server framework decodes automatically.

How This Relates to Neighbours

To see what a character looks like and its code point, use the in-site ASCII page; this one focuses on percent-encoding in URL context.

For full encode/decode work (base64, query string parsing, form serialisation), use the in-site URL tools; this table is only a character-level reference.

Why a Chinese character becomes three %XX sequences belongs to the Character Encodings page; this one covers ASCII symbols only.

If the goal is debugging a specific redirect problem, what you usually need is full request/response tracing rather than a character encoding table.

Practical Snippet

Key points: (1) a space may be + in a query string but must be %20 in a path, where + is a literal plus; (2) a valid encoded result never contains %25 — seeing it means double encoding, fixed by agreeing that exactly one layer encodes and the rest pass through; (3) prefer URLSearchParams, which correctly encodes & and = so parameters cannot shift; (4) the address bar shows a normalised value, so debug encoding against the Network Request URL and server logs.

// ① 空格:查询串用 +,路径用 %20
const q = 'keyword=' + encodeURIComponent('a b')  // keyword=a%20b(函数一律用 %20,更安全)
const legacyForm = 'keyword=a+b'                // 仅在 form-urlencoded 语境下等价于空格
// ⚠️ 路径里绝不能用 + 表示空格:
new URL('https://x.dev/a b').pathname            // /a%20b
new URL('https://x.dev/' + 'a+b').pathname       // /a+b —— 服务器会当成字面加号

// ② 检测双重编码:合法编码结果不含 %25
const twice = encodeURIComponent('%20')           // '%2520'  ← % 被编码成 %25
console.log(twice.includes('%25'))                // true ⇒ 多编码了

// ③ 只在约定的一层编码,其余透传
function buildQuery(params) {
  const p = new URLSearchParams()
  for (const [k, v] of Object.entries(params)) p.append(k, v)   // 内部正确编码
  return p.toString()                                          // keyword=a+b
}
console.log(buildQuery({ keyword: 'a b', q: 'x&y' }))
// keyword=a+b&q=x%26y  —— & 已被编码,参数不会串位

// ④ 调试时看原始 URL,不看地址栏显示值
console.log(location.href)                      // 浏览器可能已规范化
console.log(new URL(location.href).pathname)     // 规范化后的路径
// 排编码问题:以 Network 面板的 Request URL 与服务端日志为准
CharEncodedNote
space%20In forms also written as + (application/x-www-form-urlencoded)
!%21Exclamation mark
#%23Fragment identifier delimiter
$%24Dollar sign
&%26Query parameter delimiter
'%27Single quote
(%28Left parenthesis
)%29Right parenthesis
*%2AAsterisk
+%2BPlus (represents space in forms)
,%2CComma
/%2FPath delimiter
:%3AScheme/host delimiter
;%3BParameter delimiter
=%3DKey-value delimiter
?%3FQuery start
@%40Userinfo delimiter
[%5BLeft square bracket
]%5DRight square bracket
%%25Percent itself must be encoded
"%22Double quote
<%3CLess-than sign
>%3EGreater-than sign
\%5CBackslash
^%5ECaret
`%60Backtick
{%7BLeft curly brace
|%7CVertical bar
}%7DRight curly brace
~%7ETilde

Frequently Asked Questions

Why is a space sometimes %20 and sometimes +?

In application/x-www-form-urlencoded (form submit) a space becomes +; in path/query percent-encoding a space is always %20. Different contexts, do not mix them.

Which characters must be encoded?

RFC 3986 reserved characters (: / ? # [ ] @ ! $ & ' ( ) * + , ; =) must be encoded when not used as delimiters; also any non-ASCII character (e.g. Chinese) must be encoded.

Why are there two hex digits after %?

Percent-encoding represents a byte as %XX. Non-ASCII chars are first UTF-8 encoded into bytes, then each byte becomes %XX.

What is the difference between URL encoding and Base64?

URL encoding only escapes unsafe characters and keeps structure readable, for URLs. Base64 encodes arbitrary binary into text and changes the content, for transferring binary.