URL Encoding Reference

Percent-encoding rules and character tables

How Percent-Encoding Works

Characters not allowed in URLs are converted to %XX format, where XX is the hexadecimal byte value.

Character:    space       é              中
UTF-8 bytes:  0x20        0xC3 0xA9      0xE4 0xB8 0xAD
Encoded:      %20         %C3%A9         %E4%B8%AD
Multi-byte characters produce multiple %XX sequences

Character Categories

Category Characters Encoding Required?
Unreserved A-Z a-z 0-9 - . _ ~ Never encode
Reserved (gen-delims) : / ? # [ ] @ Encode when used as data
Reserved (sub-delims) ! $ & ' ( ) * + , ; = Encode when used as data
Unsafe space " < > { } | \ ^ ` Always encode
Non-ASCII All characters above 0x7F Always encode

Common Character Encodings

Char Encoded Char Encoded Char Encoded Char Encoded
space %20 ! %21 " %22 # %23
$ %24 % %25 & %26 ' %27
( %28 ) %29 * %2A + %2B
, %2C / %2F : %3A ; %3B
< %3C = %3D > %3E ? %3F
@ %40 [ %5B \ %5C ] %5D
^ %5E ` %60 { %7B | %7C
} %7D ~ %7E

JavaScript Encoding Functions

Function Purpose Preserves Use For
encodeURIComponent() Encode a URI component A-Za-z0-9 - _ . ! ~ * ' ( ) Query values, path segments
encodeURI() Encode a complete URI A-Za-z0-9 ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) # Full URLs (rarely needed)
decodeURIComponent() Decode a URI component Decoding query values
decodeURI() Decode a complete URI Decoding full URLs
// Encoding examples
encodeURIComponent("hello world")     → "hello%20world"
encodeURIComponent("a=1&b=2")         → "a%3D1%26b%3D2"
encodeURIComponent("user@example.com") → "user%40example.com"

encodeURI("https://example.com/path?q=hello world")
→ "https://example.com/path?q=hello%20world"

// Decoding examples
decodeURIComponent("hello%20world")   → "hello world"
decodeURIComponent("%E4%B8%AD%E6%96%87") → "中文"
Common encoding/decoding operations

Space Encoding: %20 vs +

Spaces can be encoded two ways depending on context:

Encoding Context Standard Example
%20 URL path, most contexts RFC 3986 /hello%20world
+ HTML form data (query strings) application/x-www-form-urlencoded ?name=hello+world
// URLSearchParams uses + for spaces
new URLSearchParams({name: "hello world"}).toString()
→ "name=hello+world"

// encodeURIComponent uses %20
encodeURIComponent("hello world")
→ "hello%20world"

// Both decode correctly
decodeURIComponent("hello+world")   → "hello+world" (+ preserved!)
decodeURIComponent("hello%20world") → "hello world"

// Use URLSearchParams for form data
const params = new URLSearchParams("name=hello+world");
params.get("name") → "hello world"
Space encoding behavior differences

International Characters (UTF-8)

Non-ASCII characters are first converted to UTF-8 bytes, then percent-encoded:

Character Unicode UTF-8 Bytes Percent-Encoded
é U+00E9 C3 A9 %C3%A9
ñ U+00F1 C3 B1 %C3%B1
U+4E2D E4 B8 AD %E4%B8%AD
U+65E5 E6 97 A5 %E6%97%A5
🎉 U+1F389 F0 9F 8E 89 %F0%9F%8E%89

Encoding Rules by Component

Component Must Encode Safe Characters
Path segment Reserved chars if used as data unreserved + : @ ! $ & ' ( ) * + , ; =
Query key = & and reserved if data unreserved + : @ ! $ ' ( ) * + , ; /
Query value = & and reserved if data unreserved + : @ ! $ ' ( ) * + , ; / ?
Fragment Non-URI characters unreserved + : @ ! $ & ' ( ) * + , ; = / ?
Userinfo @ : and reserved unreserved + ! $ & ' ( ) * + , ; =

Common Encoding Mistakes

Mistake Problem Correct Approach
Double encoding %25252F instead of %2F Encode once, at the boundary
Using encodeURI() for values Doesn't encode = & Use encodeURIComponent()
Manual string building Missing edge cases Use URLSearchParams or URL API
Encoding already-encoded % becomes %25 Decode first if unsure
Not encoding + Interpreted as space in queries Encode as %2B