Reference library

URL and percent-encoding: characters, query parameters, and examples

URL anatomy, RFC 3986 reserved characters, UTF-8 percent-encoding, query parameters, and URLSearchParams examples.

18 characters
RFC 398618reserved characters
Safe setA–ZA–Z · a–z · 0–9 · - . _ ~
EncodingUTF‑8one %HH triplet per byte
Parse before encoding

A URL is a set of components

The same character can be syntax in one component and ordinary data in another. Encode the value at the boundary where it is inserted.

  1. SchemehttpsSelects the protocol.
  2. Hostexample.comNames the server; domains are serialized separately from path text.
  3. Port443Optional network port after the host.
  4. Path/catalog/caf%C3%A9A sequence of slash-separated segments.
  5. Query?q=coffee&tag=urlApplication-defined name/value data after ?.
  6. Fragment#examplesClient-side position or state after #; normally not sent in HTTP.
RFC 3986

Reserved characters and their percent form

A reserved character may remain literal when it performs its defined delimiter role. When it is data inside a component, encode it so it cannot change the URL structure.

RFC 3986 reserved URL characters
CharacterGroupHEXPercentTypical role
:Colon
gen-delims 3A
Separates the scheme, port, and userinfo parts.
/Slash
gen-delims 2F
Separates path segments; two slashes introduce the authority.
?Question mark
gen-delims 3F
Starts the URL query component.
#Number sign
gen-delims 23
Starts the fragment; it is normally not sent to the server.
[Left square bracket
gen-delims 5B
Opens an IPv6 literal in the host.
]Right square bracket
gen-delims 5D
Closes an IPv6 literal in the host.
@At sign
gen-delims 40
Separates userinfo from the host.
!Exclamation mark
sub-delims 21
A sub-delimiter whose exact meaning is scheme- or component-specific.
$Dollar sign
sub-delims 24
A sub-delimiter that can have scheme-specific meaning.
&Ampersand
sub-delims 26
Commonly separates query parameter pairs.
'Apostrophe
sub-delims 27
A sub-delimiter without one universal meaning.
(Left parenthesis
sub-delims 28
A sub-delimiter without one universal meaning.
)Right parenthesis
sub-delims 29
A sub-delimiter without one universal meaning.
*Asterisk
sub-delims 2A
A sub-delimiter; URLSearchParams normally leaves it as *.
+Plus sign
sub-delims 2B
Represents a space in form encoding; encode a literal plus as %2B.
,Comma
sub-delims 2C
A sub-delimiter whose meaning depends on the API or scheme.
;Semicolon
sub-delims 3B
A sub-delimiter sometimes used for path-segment parameters.
=Equals sign
sub-delims 3D
Commonly separates a query parameter name from its value.
UTF‑8 → %HH

Percent-encoding examples

First turn the character into UTF-8 bytes, then write every encoded byte as % followed by two hexadecimal digits.

UTF-8 percent-encoding examples
InputUTF‑8EncodedWhy
SPSpace
20
Becomes + in URLSearchParams.
+Literal plus
2B
Must be distinguished from a form-encoded space.
&Ampersand in a value
26
Otherwise it looks like a parameter separator.
=Equals sign in a value
3D
Otherwise it can look like a name/value boundary.
/Slash as data
2F
Inside one component it must not split the path.
#Number sign as data
23
Otherwise it starts the fragment.
%Percent sign
25
Starts a percent triplet and must itself be encoded.
éLatin é
C3 A9
Two UTF-8 bytes produce two triplets.
яCyrillic ya
D1 8F
Encoded byte by byte in UTF-8.
😀Emoji
F0 9F 98 80
Four UTF-8 bytes produce four triplets.
application/x-www-form-urlencoded

Build query parameters as tuples

Do not concatenate user data with &, =, and + by hand. URLSearchParams preserves the name/value boundary and repeated keys.

Construct
const url = new URL("https://example.com/search");
url.searchParams.set("q", "кофе & чай");
url.searchParams.append("tag", "url");
url.searchParams.append("tag", "encoding");

url.toString();
Read correctly
  • params.get("tag")

    returns only the first value

  • params.getAll("tag")

    returns ["url", "encoding"]

  • params.has("debug")

    checks presence separately from its value

  • decodeURIComponent("a+b")

    returns a+b, not a space; use URLSearchParams for form-style query parsing

Choose the boundary

Which browser API to use

new URL()whole URL

Parse, resolve, and serialize a complete URL. Set pathname, searchParams, and hash through their dedicated properties.

const url = new URL("/search", "https://example.com");
URLSearchParamsquery

Build or read query tuples. It uses application/x-www-form-urlencoded: spaces become + and literal plus signs become %2B.

new URLSearchParams({ q: "coffee & tea" }).toString()
encodeURIComponent()one component

Encode text that will become one URL component. A space becomes %20; do not apply it to an already assembled URL.

encodeURIComponent("a/b?c=d") // a%2Fb%3Fc%3Dd
encodeURI()complete URI text

Keeps URI delimiters such as /, ?, #, &, and =. It does not make an untrusted query value safe for concatenation.

encodeURI("https://example.com/a b?q=x")
Primary specifications

Standards behind the tables

Related tools

Continue working with text

Copy a symbol, convert markup, clean a fragment, or keep the result in the notebook.

FAQ

Questions about URL encoding

Why does a space sometimes become %20 and sometimes +?

%20 is the percent-encoded UTF-8 byte for a space. The application/x-www-form-urlencoded format used by URLSearchParams serializes spaces as +. Both conventions occur in query strings, but + is not a general replacement for spaces in every URL component.

How do I put a literal plus sign in a query value?

Use URLSearchParams or encode it as %2B. A form-style query parser interprets a raw + as a space.

Should I encode a whole URL with encodeURIComponent()?

No. It encodes structural delimiters and turns a whole URL into one component. Use new URL() for the whole URL and URLSearchParams or encodeURIComponent() only for the specific value boundary.

Can query parameters repeat?

Yes. A query is an ordered list of name/value tuples, so tag=url&tag=encoding is valid. Use append() to add values and getAll() to read every repeated value.

Should percent triplets use uppercase hex?

Hex digits are case-insensitive, but RFC 3986 recommends uppercase A–F for normalization, such as %2F rather than %2f.