URL Encoding & Percent-Encoding Reference
Percent-encoding represents arbitrary octets as a percent sign (%) followed by two hexadecimal digits. Non-ASCII characters are converted to UTF-8 bytes before being percent-encoded according to the specific URL component's percent-encode set.
Percent-Encoding Inspector
hello%20world
UTF-8 Byte Pipeline: How Characters Become Percent-Encoded
WHATWG URL §4.4 & Unicode 17.0
Percent-encoding operates strictly on 8-bit byte octets, not on Unicode characters directly. When a non-ASCII character is entered into a URL, it is first serialized into its UTF-8 octets, and each resulting octet is then represented as a percent sign (%) followed by two uppercase hexadecimal digits.
A common technical mistake is assuming that U+20AC becomes %20AC. That produces a space (%20) followed by the literal letters AC! In standards-conforming URL processing, every non-ASCII character must be mapped to UTF-8 bytes first.
Space Handling: When to Use %20 vs +
Critical Disambiguation
One of the most frequent sources of developer confusion is whether spaces in URLs should be encoded as %20 or +. The answer is strictly context-dependent:
Path, Fragment & Component Encoding
Space is encoded as %20
hello world
hello%20world
+:
a+b → a+b (preserved)
- Used by
encodeURIComponent()andencodeURI(). - Required in URL paths (e.g.
/articles/my%20story/). - In this context,
+represents a literal plus sign, not a space.
application/x-www-form-urlencoded
Space is encoded as +
hello world
hello+world
+:
a+b → a%2Bb (escaped!)
- Used by HTML form submissions (
POST/GET) andURLSearchParams. - Used in query strings formatted as form data (e.g.
?q=my+search). - Because
+means space, a literal plus sign must be encoded as%2B.
If a user searches for C++ in a query parameter formatted with application/x-www-form-urlencoded, sending ?q=C++ causes the server to receive C (two spaces)! To preserve the plus signs, the client must serialize it as ?q=C%2B%2B.
Context-Aware Character Reference Table
Standards MatrixUnlike static tables that show a single value, this reference matrix displays how reserved delimiters and special characters are encoded across different URL components:
| Char | Name & Code Point | UTF-8 Octets | Component (encodeURIComponent) |
Form Data (URLSearchParams) |
Path Segment | Structural Role in URLs |
|---|---|---|---|---|---|---|
| Space |
Space
U+0020
|
20
|
%20
|
+
|
%20
|
Delimiter in HTTP headers, illegal unencoded in URIs |
| & |
Ampersand
U+0026
|
26
|
%26
|
%26
|
%26
|
Separates parameter pairs in query string and form data |
| + |
Plus Sign
U+002B
|
2B
|
%2B
|
%2B
|
%2B
|
Represents space in form-urlencoded; literal plus must be %2B |
| / |
Forward Slash (Solidus)
U+002F
|
2F
|
%2F
|
%2F
|
%2F
|
Hierarchical segment delimiter in URI path |
| ? |
Question Mark
U+003F
|
3F
|
%3F
|
%3F
|
%3F
|
Introduces query string component |
| # |
Number Sign (Hash)
U+0023
|
23
|
%23
|
%23
|
%23
|
Introduces fragment/anchor identifier |
| = |
Equals Sign
U+003D
|
3D
|
%3D
|
%3D
|
%3D
|
Separates parameter name from value in query string |
| % |
Percent Sign
U+0025
|
25
|
%25
|
%25
|
%25
|
Introduces percent-encoded byte triplets (%HH) |
| : |
Colon
U+003A
|
3A
|
%3A
|
%3A
|
%3A
|
Terminates scheme; separates userinfo and port number |
| @ |
At Sign
U+0040
|
40
|
%40
|
%40
|
%40
|
Separates userinfo credentials from host authority |
| € |
Euro Sign
U+20AC
|
E2 82 AC
|
%E2%82%AC
|
%E2%82%AC
|
%E2%82%AC
|
Requires 3 UTF-8 bytes (E2 82 AC) before percent-encoding |
| é |
Latin Small Letter E with Acute
U+00E9
|
C3 A9
|
%C3%A9
|
%C3%A9
|
%C3%A9
|
Requires 2 UTF-8 bytes (C3 A9) before percent-encoding |
| 😀 |
Grinning Face Emoji
U+1F600
|
F0 9F 98 80
|
%F0%9F%98%80
|
%F0%9F%98%80
|
%F0%9F%98%80
|
Requires 4 UTF-8 bytes (F0 9F 98 80) before percent-encoding |
JavaScript & Web API Comparison: Which Function Should You Use?
Developer GuideChoosing the wrong encoding function in JavaScript is one of the most common causes of broken URLs and security vulnerabilities. Below is the authoritative standards comparison:
encodeURIComponent()
JavaScript (ECMAScript)
):
%20
/):
Encoded as %2F
encodeURI()
JavaScript (ECMAScript)
):
%20
/):
Preserved as /
URLSearchParams
Web Standards API (Browser & Node.js)
):
+ (plus sign)
/):
Encoded as %2F
new URL(href)
WHATWG URL Standard (Browser & Node.js)
):
%20 in path and hash; + in searchParams
/):
Preserves structural path separators
Recommended Modern Implementation Pattern
// 1. Parameter Value Encoding (Safe for query values)
const rawParam = "hello/world & co";
const encodedParam = encodeURIComponent(rawParam);
// → "hello%2Fworld%20%26%20co"
// 2. Query String Construction with URLSearchParams (Best practice)
const params = new URLSearchParams({ search: "C++ language", filter: "active" });
// → "search=C%2B%2B+language&filter=active" (Note: + for space, %2B for literal plus)
// 3. Complete URL Building with new URL()
const url = new URL("https://example.com/api/search");
url.search = params.toString();
// → "https://example.com/api/search?search=C%2B%2B+language&filter=active"
Edge Cases: Malformed Escapes & Double-Encoding Traps
Security & ParsingMalformed Percent Sequences
Under the WHATWG URL Standard §4.4, if a percent sign (%) is not followed by two ASCII hexadecimal digits, it is preserved as a literal character rather than throwing a fatal error.
| Malformed Input | WHATWG Decoding | Validator Warning |
|---|---|---|
100% |
100% |
Lone percent sign; should be encoded as %25 if literal data |
%2 |
%2 |
Incomplete escape; missing second hex digit |
%GG |
%GG |
Non-hexadecimal characters following % |
%20 (double encode) |
%2520 |
% encoded as %25; double encoding alters data meaning |
Double-Encoding Security Trap
Because the percent sign is itself encoded as %25, encoding an already-encoded string changes its meaning:
hello world
hello%20world
hello%2520world
Decoders must execute strictly one decode pass per action to prevent security vulnerabilities like directory traversal (%252E%252E%252F → %2E%2E%2F).