Why URLs need encoding
A URL is not just text — it is a structured identifier with specific delimiters (://, /, ?, &, =, #) that have precise meanings. If you want to pass data that contains any of these characters, or any character outside the basic ASCII set, you must encode it so the URL parser cannot mistake your data for a structural delimiter.
The encoding format is defined by RFC 3986 (the authoritative URI standard): replace each unsafe character with % followed by its hexadecimal byte value in UTF-8. The result is called a percent-encoded string, and the process is called percent-encoding or URL encoding.
The Query String Builder handles this encoding for you — you paste in plain-text key-value pairs and get back a correctly encoded query string. This guide explains the rules it follows, so you understand why the output looks the way it does.
Safe vs unsafe characters
RFC 3986 defines three categories:
Unreserved characters — never need encoding
These are always safe anywhere in a URI: A–Z, a–z, 0–9, and the four symbols -, _, ., ~. A well-written encoder will not touch these.
Reserved characters — must be encoded when used as data
These characters have structural meaning in a URL: :, /, ?, #, [, ], @, !, $, &, ', (, ), *, +, ,, ;, =. When you want to include them as literal data (not as delimiters), they must be percent-encoded.
Other characters — always need encoding Spaces, Unicode characters (accented letters, emoji, non-Latin scripts), control characters, and anything else not in the two groups above must always be percent-encoded.
How percent-encoding works step by step
Take a space character:
- Its Unicode code point is U+0020.
- In UTF-8 it is a single byte:
0x20. - The percent-encoded form is
%20.
Take the euro sign €:
- Its Unicode code point is U+20AC.
- In UTF-8 it is three bytes:
0xE2 0x82 0xAC. - The percent-encoded form is
%E2%82%AC.
Take the letter é (e with acute):
- U+00E9.
- Two UTF-8 bytes:
0xC3 0xA9. - Percent-encoded:
%C3%A9.
Everything else follows the same pattern: take the UTF-8 bytes of the character and write % before each byte.
Query strings in particular
When building a query string, you encode the keys and values separately — not the whole URL, and not the ?, &, and = delimiters. Those remain literal:
https://example.com/search?q=hello%20world&city=S%C3%A3o%20Paulo
│─────────────│ │─────────────────────│
key=value key=value (encoded separately)
The Query String Builder does exactly this: it percent-encodes each key and value individually and joins them with the literal &.
The + space problem
There are two conventions for encoding a space in a query string:
RFC 3986: encode as %20. This is the standard for URLs in general and for API clients.
application/x-www-form-urlencoded: encode as +. This is what browsers use when submitting an HTML <form method="GET">. The server is expected to decode + back to a space.
The two conventions conflict: if your data contains a literal +, it must be encoded as %2B under form encoding to distinguish it from a space. Under RFC 3986, a literal + does not need encoding (it is in the reserved set but not a structural delimiter in query strings), but it is safest to encode it as %2B in either case.
Practical rule: use RFC 3986 (%20) for new code. Use form encoding only when explicitly required by a system that expects it. The Query String Builder lets you switch between them.
Common mistakes
Encoding the full URL instead of just the values
// WRONG — encodes the : and // in the scheme
encodeURIComponent('https://example.com/search?q=hello world');
// → 'https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world'
// RIGHT — encode only the value
'https://example.com/search?q=' + encodeURIComponent('hello world');
// → 'https://example.com/search?q=hello%20world'
Double-encoding
// If the value is already encoded, encoding again produces %25xx
encodeURIComponent('hello%20world');
// → 'hello%2520world' — %25 is the encoding of %
The Query String Builder’s parse mode reveals double-encoding: you will see %25 in the raw column alongside the original %20 in the decoded column.
Not encoding Unicode
ASCII-only test data masks encoding bugs. The real issue surfaces when a user enters a name like André, a city like São Paulo, or a search term in Japanese or Arabic. Always test with Unicode values in both directions.
Encoding structural characters that should stay literal
// WRONG — encodes the whole path
fetch(encodeURIComponent('/api/search?q=hello'));
// RIGHT — use the URL constructor or encode only the query value
const url = new URL('/api/search', 'https://example.com');
url.searchParams.set('q', 'hello world');
// url.href → 'https://example.com/api/search?q=hello+world'
// Note: URLSearchParams uses form-encoding (+ for spaces)
How different languages handle it
JavaScript (browser/Node.js)
URLSearchParams is the idiomatic choice — it handles encoding automatically:
const p = new URLSearchParams({ q: 'hello world', tag: 'C# dev' });
p.toString(); // q=hello+world&tag=C%23+dev (form-encoded)
For RFC-3986 encoding, use encodeURIComponent on each value manually:
const qs = Object.entries({ q: 'hello world' })
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
// q=hello%20world
Python
from urllib.parse import urlencode, quote
urlencode({'q': 'hello world'}) # q=hello+world (form)
quote('hello world', safe='') # hello%20world (RFC-3986)
Go
u := url.Values{}
u.Set("q", "hello world")
u.Encode() // q=hello+world (form)
Decoding
To reverse the encoding, replace each %xx sequence with the corresponding byte and decode the resulting byte sequence as UTF-8. In JavaScript: decodeURIComponent('hello%20world') → hello world. Watch out for malformed sequences (e.g. a bare % not followed by two hex digits) — robust decoders catch the error and leave the sequence unchanged rather than throwing.
Summary
Percent-encoding keeps URLs unambiguous by replacing unsafe characters with %xx sequences. The unreserved characters (ASCII letters, digits, -_. ~) never need encoding. Reserved characters need encoding when used as data. Spaces become %20 (RFC-3986) or + (form encoding). Encode keys and values individually, not the whole URL. To build or decode a query string quickly and correctly, use the Query String Builder.