Every time you submit a form, click a link with query parameters, or make an API call, URL encoding is working behind the scenes. It's one of the most fundamental mechanisms in web development—yet it's also one of the most misunderstood. Double-encoding bugs, incorrect character handling, and confusion between encoding functions cause countless hours of debugging.
This guide covers URL encoding from first principles to advanced edge cases. Whether you're a beginner building your first API or a senior developer debugging encoding issues in a production system, you'll find practical answers here.
Need to quickly encode or decode a URL? Try our client-side tool.
Try Risetop URL Encoder/Decoder →URL encoding (also called percent-encoding) is a mechanism for encoding arbitrary data in a URI using only the limited US-ASCII character set that is legal in URIs. Defined in RFC 3986, it replaces unsafe characters with a percent sign (%) followed by two hexadecimal digits representing the character's byte value.
For example:
Original: hello world & foo=bar
Encoded: hello%20world%20%26%20foo%3Dbar
URLs were designed in the early 1990s for a simple internet. They can only contain a subset of ASCII characters. But modern web applications need to pass all kinds of data in URLs—Chinese characters, emoji, spaces, ampersands, and more. URL encoding bridges this gap by converting any byte sequence into URL-safe ASCII.
There are two categories of characters in a URL:
A-Z a-z 0-9 - _ . ~ — These never need encoding! * ' ( ) ; : @ & = + $ , / ? % # [ ] — These have special meaning in URLs and must be encoded when used as dataUnderstanding which characters get encoded and how is crucial. Here's the complete reference:
| Character | Encoded | Category |
|---|---|---|
| Space | %20 | Must encode |
| ! | %21 | Sub-delim (reserved) |
| # | %23 | Fragment delimiter |
| $ | %24 | Sub-delim |
| & | %26 | Query separator |
| ' | %27 | Sub-delim |
| ( | %28 | Sub-delim |
| ) | %29 | Sub-delim |
| + | %2B | Sub-delim (note: + means space in query strings) |
| , | %2C | Sub-delim |
| / | %2F | Path separator |
| : | %3A | Scheme/authority separator |
| ; | %3B | Sub-delim |
| = | %3D | Query key-value separator |
| ? | %3F | Query start delimiter |
| @ | %40 | Authority delimiter |
| [ | %5B | IPv6 literal delimiter |
| ] | %5D | IPv6 literal delimiter |
The key insight that many developers miss is that encoding depends on which part of the URL you're in. A character that's safe in one component may be dangerous in another.
https://example.com/path?query=value#fragment
\___/ \________/ \_________/ \______/
scheme host query fragment
\__________/
authority
a-z, 0-9, +, -, . are allowed/ is the delimiter; encode everything else that's not unreserved& and = are delimiters; encode them in values. + is conventionally decoded as space+ conventionJavaScript provides three built-in encoding functions, and choosing the wrong one is a common source of bugs:
Encodes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( )
encodeURIComponent("hello world&foo=bar")
// "hello%20world%26foo%3Dbar"
encodeURIComponent("search?q=javascript tutorial")
// "search%3Fq%3Djavascript%20tutorial"
Use for: Query parameter values, individual path segments, form data.
Encodes all characters except: A-Z a-z 0-9 ; / ? : @ & = + $ , # - _ . ! ~ * ' ( )
encodeURI("https://example.com/path with spaces")
// "https://example.com/path%20with%20spaces"
encodeURI("https://example.com/search?q=hello world")
// "https://example.com/search?q=hello%20world"
Use for: Full URLs where you want to preserve the structure (scheme, host, delimiters).
const url = "https://example.com/api?key=hello world";
encodeURI(url);
// "https://example.com/api?key=hello%20world" ✅ Preserves ? and =
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fapi%3Fkey%3Dhello%20world" ❌ Breaks the URL
The rule is simple: encodeURI for full URLs, encodeURIComponent for values that go inside URLs.
The most frequent encoding bug. When data gets encoded twice:
// First encoding: space → %20
encodeURIComponent("hello world") → "hello%20world"
// Second encoding: % → %25, so %20 → %2520
encodeURIComponent("hello%20world") → "hello%2520world"
This results in the user seeing literal %20 instead of a space. Always check whether your input is already encoded before encoding again.
The application/x-www-form-urlencoded format (used by HTML forms) encodes spaces as +. The URI standard uses %20. This mismatch causes subtle bugs:
// Server receives: search?q=hello+world
// Is the user searching for "hello world" or "hello+world"?
// In application/x-www-form-urlencoded: it's "hello world"
// In a raw URL: it depends on the server implementation
Most modern frameworks handle both, but don't rely on it. Be explicit in your encoding and decoding.
JavaScript's encoding functions use UTF-8 encoding for non-ASCII characters:
encodeURIComponent("你好")
// "%E4%BD%A0%E5%A5%BD"
// 你 → U+4F60 → UTF-8: E4 BD A0
// 好 → U+597D → UTF-8: E5 A5 BD
Each Unicode character is encoded as its UTF-8 byte sequence, then each byte is percent-encoded. This is why a single Chinese character becomes 9 characters in a URL.
from urllib.parse import quote, quote_plus, unquote
quote("hello world&foo=bar")
# 'hello%20world%26foo%3Dbar'
quote_plus("hello world&foo=bar")
# 'hello+world%26foo%3Dbar' # Spaces become +
unquote("hello%20world")
# 'hello world'
Note the difference: quote() uses %20 for spaces (URI standard), while quote_plus() uses + (form encoding).
encodeURIComponent("hello world") // Same as browser
// "hello%20world"
// Node.js also provides:
const querystring = require('querystring');
querystring.stringify({q: "hello world"});
// 'q=hello+world' // Note: uses + for spaces
// Use URLSearchParams for modern code:
const params = new URLSearchParams({q: "hello world"});
params.toString(); // 'q=hello+world'
Here's a reliable workflow for handling URLs in web applications:
encodeURIComponent() when inserting into query parameters.While you should always use proper encoding functions in code, online URL encoder/decoder tools are invaluable for:
An online tool like Risetop's URL Encoder/Decoder lets you paste a URL or string, instantly see the encoded/decoded version, and copy the result. Since it runs entirely in your browser, your data stays private.
URL encoding converts unsafe characters in a URL into a percent sign (%) followed by two hexadecimal digits. For example, a space becomes %20 and an ampersand becomes %26. This ensures URLs only contain ASCII characters that are safe for transmission over the internet.
encodeURI() is designed to encode full URLs and preserves characters like :/?#&= that have special meaning in URL structure. encodeURIComponent() encodes ALL non-alphanumeric characters including those structural characters, making it suitable for encoding query parameter values and path segments.
In the path portion of a URL, use %20 (this is what encodeURI produces). In query strings, both + and %20 are commonly used—the + convention comes from application/x-www-form-urlencoded encoding. Modern frameworks like encodeURIComponent use %20 for spaces. Most servers accept both, but %20 is more universally correct.
Double encoding is the most common issue—when encoded text gets encoded again, producing values like %2520 instead of %20. This happens when you encode a string that's already encoded. Always decode first if you're unsure whether input is already encoded, and encode exactly once before sending data.
Only encode the specific components that may contain unsafe characters—typically query parameter values, path segments with user input, and fragment identifiers. Never encode the entire URL at once, as this would encode structural characters like :// and / that define the URL's meaning.