Understanding URL Encoding: Why %20 Means Space
URL encoding, also called percent-encoding, is the rule that lets URLs carry spaces, Unicode text, symbols, and reserved characters without breaking the URL structure.
If you have copied a link and seen %20, %2F, %3F, %E4%B8%AD, or %F0%9F%98%80, you have seen URL encoding in action. It looks noisy, but it solves a practical problem: a URL is not just text. Characters such as ?, &, =, /, and # have structural meaning inside a URL.
Why URL Encoding Exists
A URL is split into parts: scheme, host, path, query string, and fragment.
https://example.com/search?q=hello%20world&page=1#results
In that URL:
httpsis the scheme.example.comis the host./searchis the path.q=hello%20world&page=1is the query string.resultsis the fragment.
If a query value contains a space, ampersand, slash, Chinese character, or emoji, the browser and server need a safe way to represent it. Percent-encoding converts the UTF-8 bytes of the character into % plus two hexadecimal digits.
Common Encoded Characters
| Character | Encoded |
|---|---|
| Space | %20 |
/ | %2F |
? | %3F |
& | %26 |
= | %3D |
# | %23 |
中 | %E4%B8%AD |
A space is commonly shown as %20. In application/x-www-form-urlencoded form data, spaces may also appear as +. That difference is a frequent source of bugs when developers copy values between forms, URLs, and API clients.
Reserved characters vs normal text
Some characters are allowed in URLs but have a structural meaning. These are called reserved characters. For example:
?starts the query string.&separates query parameters.=separates a parameter name from its value./separates path segments.#starts the fragment.
If one of these characters is part of user input, it must be encoded at the correct layer. The string red & blue can be a normal search value, but & has a different meaning inside a query string. The URL layer cannot guess your intent; your code must encode the value before adding it to the URL.
Unicode characters work the same way. A character such as 中 is first represented as UTF-8 bytes, then each byte is written as % plus two hex digits. That is why non-English text can expand into several percent-encoded bytes.
encodeURI vs encodeURIComponent
JavaScript provides two commonly used encoding functions, and choosing the wrong one can break a URL.
Use encodeURI() when you already have a complete URL and want to encode characters that are unsafe in the whole URL:
const url = 'https://example.com/search?q=hello world'
encodeURI(url)
// https://example.com/search?q=hello%20world
Use encodeURIComponent() for a single URL component, especially a query parameter value:
const value = 'hello world & JSON'
encodeURIComponent(value)
// hello%20world%20%26%20JSON
The difference matters because encodeURI() leaves reserved URL characters such as ?, &, /, and = alone. That is useful for a complete URL, but dangerous for a query parameter value.
Query Parameter Example
Suppose you want to build this search URL:
https://example.com/search?q=hello world & JSON&page=1
If the q value is not encoded, the & inside the search text may be interpreted as the start of a new parameter. The safer version is:
const q = 'hello world & JSON'
const url = `https://example.com/search?q=${encodeURIComponent(q)}&page=1`
Result:
https://example.com/search?q=hello%20world%20%26%20JSON&page=1
Now the server receives one q value instead of accidentally splitting it into multiple parameters.
Server-side parsing matters
Encoding is only half of the workflow. The server also needs to parse and decode the URL in the right order.
A safe server-side flow is:
- Parse the URL into path, query string, and fragment.
- Split the query string into parameter names and values.
- Decode each name and value once.
- Pass decoded values to the application.
Problems happen when a server decodes too early or too many times. For example, %252F decodes once to %2F and twice to /. If / is meaningful in your application route, a double-decoding bug can change behavior or create security issues.
For public pages, stable encoding also helps crawlers and analytics tools understand URLs. The same page should not appear under many double-encoded or partially encoded versions.
Nested URLs and Redirect Parameters
Another common case is placing a full URL inside a query parameter:
const next = 'https://example.com/dashboard?tab=billing&plan=pro'
const loginUrl = `/login?next=${encodeURIComponent(next)}`
The nested URL must be encoded as a component. If it is not encoded, the &plan=pro part may be interpreted as a parameter of /login instead of part of the next value.
Encoding JSON and Base64 in URLs
Sometimes a URL parameter needs to carry structured data. Common examples include filters, redirect state, small payloads, and signed tokens.
JSON should be serialized first, then encoded as a query value:
const filter = { status: 'paid', region: 'US & Canada' }
const url = `/orders?filter=${encodeURIComponent(JSON.stringify(filter))}`
On the receiving side, decode the parameter once, then parse the JSON:
const filter = JSON.parse(decodeURIComponent(value))
Base64 values need attention because standard Base64 may contain +, /, and =. These characters can be misunderstood in query strings or paths. You can either encode the Base64 value with encodeURIComponent() or use Base64URL, which replaces + and / with URL-safer characters.
Do not mix these layers randomly. Decode the URL layer first, then decode Base64, then parse JSON only if the decoded text is actually JSON.
Common URL Encoding Bugs
When a link or API call behaves strangely, check for these issues:
- A query value contains
&or=but was not encoded. - A complete URL was passed to
encodeURIComponent()and became unreadable as a link. - A query value was encoded twice, turning
%20into%2520. - A form uses
+for spaces, but the receiving code expects%20. - A Base64 value contains
+,/, or=and was placed in a URL without encoding. - A JSON string was placed in a query parameter before being encoded.
Double encoding is especially easy to miss. If you see %252F, decode once and you may get %2F; decode again and you get /.
When not to encode the whole URL
Encoding the entire URL with encodeURIComponent() is usually wrong when the result is meant to be clicked as a URL:
encodeURIComponent('https://example.com/search?q=hello world')
// https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world
That output is useful only when the complete URL itself becomes a value inside another URL. If you want a normal link, encode only the parts that need encoding, or use the URL and URLSearchParams APIs.
This distinction is important for canonical URLs, redirect URLs, OAuth callback URLs, and tracking links. A canonical link should normally be a readable URL, not an encoded blob.
Try It Yourself
Use the tool below to encode and decode examples:
For structured query strings, use the URL Parameters Tool. If a query value contains JSON, validate it with the JSON Formatter after decoding. If the value is Base64, check it with the Base64 Tool before sending it through a URL.