What is URL Encoding?
URL encoding (also called percent-encoding) converts characters that are not safe in URLs into a %XX escape sequence where XX is the uppercase hexadecimal value of the byte. For example, a space (byte 0x20) becomes %20, & (byte 0x26) becomes %26, and = becomes %3D.
URLs can only safely contain the 66 "unreserved" characters defined in RFC 3986: A-Z, a-z, 0-9, and -, _, ., ~. Every other character must be percent-encoded.
Three Distinct Encoding Contexts
The encoding rules differ depending on where in the URL the value appears:
1. Query Parameter Values (`?key=value`)
The most common case. Encode the value portion of each query parameter:
Raw: Hello World! & filter=active
Encoded: Hello%20World%21%20%26%20filter%3Dactive
URL: /search?q=Hello%20World%21%20%26%20filter%3DactiveJavaScript: use encodeURIComponent()
Go: use url.QueryEscape() or url.Values
Python: use urllib.parse.quote() or urllib.parse.urlencode()
2. Path Segments (`/resource/value/action`)
Path segments preserve / as a segment separator but encode spaces and special characters:
Raw: users/John Doe/profile
Encoded: users/John%20Doe/profile
Raw: /files/report 2025.pdf
Encoded: /files/report%202025.pdf3. Form Encoding (`application/x-www-form-urlencoded`)
Used in HTML form submissions and POST body form data. Spaces become + instead of %20:
Raw: Hello World!
Form: Hello+World%21 (space → +, ! → %21)This is the default format for <form method="POST"> and the Content-Type: application/x-www-form-urlencoded request body.
JavaScript URL Encoding
// ── encodeURIComponent ── for individual query param values
const value = "Hello World! a&b=c";
const encoded = encodeURIComponent(value);
// => "Hello%20World!%20a%26b%3Dc"
const decoded = decodeURIComponent(encoded);
// => "Hello World! a&b=c"
// ── URLSearchParams ── handles encoding automatically (recommended)
const params = new URLSearchParams({
q: "hello world",
filter: "status=active",
lang: "go+python",
});
const queryString = params.toString();
// => "q=hello+world&filter=status%3Dactive&lang=go%2Bpython"
// Note: URLSearchParams uses + for spaces (form encoding)
const url = `https://api.example.com/search?${params}`;
// ── encodeURI ── for encoding a complete URL (preserves :, /, ?, &, =, #)
const rawUrl = "https://example.com/path with spaces?q=hello there";
const safeUrl = encodeURI(rawUrl);
// => "https://example.com/path%20with%20spaces?q=hello%20there"
// encodeURI does NOT encode & = ? / : — it preserves URL structure
// ── Decoding ──
const qs = "name=Ravi%20Kumar&city=S%C5%ABrat";
const parsed = new URLSearchParams(qs);
console.log(parsed.get("name")); // => "Ravi Kumar"
console.log(parsed.get("city")); // => "Sūrat"encodeURIComponent vs encodeURI
| Function | Encodes space | Encodes & = ? # / : | Use for |
|---|---|---|---|
encodeURIComponent | %20 | Yes — all structural chars | Individual query param values |
encodeURI | %20 | No — preserves URL structure | A complete URL string |
URLSearchParams | + | Yes (form encoding) | Building query strings |
Rule: always use `encodeURIComponent` for parameter values. Never use it on a full URL — it will encode the :// and / separators.
Go URL Encoding
import (
"fmt"
"net/url"
)
// Query parameter value encoding (spaces → +)
encoded := url.QueryEscape("Hello World! / test")
// => "Hello+World%21+%2F+test"
// Path segment encoding (spaces → %20, preserves /)
pathEncoded := url.PathEscape("report 2025.pdf")
// => "report%202025.pdf"
// Build a complete URL with properly encoded params (recommended)
params := url.Values{}
params.Set("q", "hello world")
params.Set("filter", "status=active")
params.Set("lang", "go+python")
baseURL, _ := url.Parse("https://api.example.com/search")
baseURL.RawQuery = params.Encode()
fmt.Println(baseURL.String())
// => "https://api.example.com/search?filter=status%3Dactive&lang=go%2Bpython&q=hello+world"
// Decode
decoded, err := url.QueryUnescape("Hello+World%21")
if err == nil {
fmt.Println(decoded) // => "Hello World!"
}Python URL Encoding
from urllib.parse import (
quote, quote_plus, unquote, unquote_plus,
urlencode, urlparse, urljoin, parse_qs
)
# quote — path-safe encoding (spaces → %20, preserves / by default)
encoded = quote("Hello World! / test")
# => "Hello%20World%21%20/%20test"
# quote with safe="" — encode everything including /
full = quote("Hello World! / test", safe="")
# => "Hello%20World%21%20%2F%20test"
# quote_plus — form encoding (spaces → +)
form = quote_plus("Hello World!")
# => "Hello+World%21"
# Build a query string
params = {"q": "hello world", "filter": "active", "page": 1}
qs = urlencode(params)
# => "q=hello+world&filter=active&page=1"
# Parse a query string
parsed = parse_qs("q=hello+world&filter=active")
print(parsed) # => {'q': ['hello world'], 'filter': ['active']}
# Decode
print(unquote("Hello%20World%21")) # => "Hello World!"
print(unquote_plus("Hello+World%21")) # => "Hello World!"Common Mistakes
The `+` vs `%20` confusion:
+ means space only in application/x-www-form-urlencoded (form data). In a URL path segment, a + is a literal plus sign, not a space. Always check which context you are encoding for.
// Query string context (form encoding): + = space
const qs = new URLSearchParams({ q: "hello world" }).toString();
// => "q=hello+world" — valid for form data
// Path segment: + ≠ space
const path = "/search/" + encodeURIComponent("hello world") + "/results";
// => "/search/hello%20world/results" — correctDouble-encoding:
Encoding an already-encoded string doubles the percent-signs:
const v = encodeURIComponent("hello world"); // "hello%20world"
encodeURIComponent(v); // "hello%2520world" — %25 is the encoding of %Always encode raw values, never encode values that are already encoded.
Not encoding when building URLs manually:
// WRONG — if name contains & or = it breaks the query string
const url = `/api/users?name=${name}&city=${city}`;
// CORRECT
const url = `/api/users?${new URLSearchParams({ name, city })}`;Sending JSON in a URL
When you need to pass a JSON payload as a URL parameter (unusual but sometimes required):
const filter = { status: "active", minScore: 90, tags: ["api", "web"] };
// Stringify, then percent-encode the entire JSON string
const encoded = encodeURIComponent(JSON.stringify(filter));
const url = `/api/users?filter=${encoded}`;
// => /api/users?filter=%7B%22status%22%3A%22active%22%2C...%7D
// On the server — decode and parse
const raw = decodeURIComponent(req.query.filter);
const filterObj = JSON.parse(raw);Note: JSON in URLs is verbose and fragile. Prefer sending filter objects as a JSON request body (POST) for complex queries.
Use JSONKit's URL Encoder/Decoder tool to encode or decode any URL component — handles all special characters including non-ASCII (Unicode) characters correctly.