URL Encoding Explained — What %20 and Other Percent Codes Mean
URLs can only contain a small set of characters. Everything else gets percent-encoded as %XX. Learn which characters are reserved, when to encode, and the practical rules for query strings, paths, and form data.
URL encoding (also called percent-encoding) replaces characters that are not safe in a URL with %XX codes. The space becomes %20, the slash becomes %2F, and so on. The reserved characters that always need encoding in a query string are: space, &, =, +, %, #, and ?. For paths, the reserved set is smaller. The Uttir URL Encoder takes any string and percent-encodes it correctly, in your browser.
A URL can only contain a small set of characters. Anything else has to be encoded as a %XX code where XX is the hexadecimal byte value. The space becomes %20. The slash becomes %2F. The ampersand becomes %26. This is percent-encoding, the foundation of how URLs work, and the source of many bugs when it is done wrong.
This guide is the practical version: which characters are reserved, when to encode, the rules for paths vs query strings, and the most common bugs. If you just need to encode or decode a string, the Uttir URL Encoder does it correctly in your browser, with no upload.
Why URLs need encoding at all
URLs were designed to be a small, clean set of characters: letters, digits, -, _, ., ~, and a few reserved characters with special meanings. The reserved characters are ! * ' ( ) ; : @ & = + $ , / ? # [ ]. They have to be in the URL grammar, but they cannot appear in the data the URL carries, because the parser would interpret them as part of the URL structure instead of as data.
The result: any character that is not "safe" or "reserved" has to be encoded as %XX where XX is the byte value in hex. For ASCII characters, this is straightforward — a space is byte 0x20, so it becomes %20. For non-ASCII characters (UTF-8), the encoding is multi-byte: the é in café is the bytes 0xC3 0xA9, so it becomes caf%C3%A9.
The reserved characters
There are two groups of reserved characters, and the rule for encoding depends on which group they are in.
Reserved characters that must be encoded in some contexts
These characters have special meaning in URLs. Whether they need encoding depends on where in the URL they appear.
&separates query parameters. In a query value, it must be encoded as%26or the parser will split the value into two parameters.=separates a parameter name from its value. In a value, it must be encoded as%3Dor the parser will split on it.+is shorthand for a space in form-encoded data (application/x-www-form-urlencoded). In any other context, it must be encoded as%2B.#starts a fragment identifier (the part after the#in a URL). It must always be encoded when it appears in data, not as a fragment marker.?starts the query string. It must always be encoded when it appears in data, not as a query separator./separates path segments. It is a valid path character but must be encoded as%2Fwhen it appears in a path segment as data, not as a separator.%starts a percent-encoded sequence. It must be encoded as%25when it appears in data, or the parser will try to read the following two characters as a hex code.
The short version: any of these characters that is data (not grammar) must be encoded. The URL encoder handles this automatically.
Characters that should always be encoded
Some characters have no special meaning but are still risky in URLs because they are not handled consistently:
- Space:
%20(or+in form data) - Control characters (0x00–0x1F, 0x7F): always encode
- Non-ASCII characters: encode as their UTF-8 bytes
Paths vs query strings, the rules
The most common bug is encoding in the wrong place. The rules differ.
In a path
Paths use / as the segment separator. The ?, #, and & characters are not path grammar and should be encoded if they appear in data. Most other characters, including spaces and most punctuation, are valid in a path but it is safer to encode them anyway (to avoid double-encoding bugs when a URL is passed around).
Example: /search?q=hello world in a path component should be /search/hello%20world if "hello world" is a path segment, or /search?q=hello%20world if it is a query value. The two are different URLs and mean different things to the server.
In a query value
Query values are the strictest. The reserved characters that must be encoded: &, =, +, %, #, and any non-ASCII. Spaces are encoded as %20 (or + in form-encoded data).
Example: a search for "cats & dogs" passed in a query value should be q=cats%20%26%20dogs. If the value is "cats & dogs" without encoding, the parser sees q=cats , dogs as two parameters, and your backend gets the wrong input.
The form-encoded data convention
For HTML form submissions with application/x-www-form-urlencoded content type, the encoding is slightly different. The space is encoded as + instead of %20. Most server frameworks know to convert + back to a space when parsing form data.
This is the source of a long-running bug: if you are using URL encoding in a different context (e.g. building a URL for a fetch call) and you encode spaces as +, the receiving server might or might not interpret it as a space, depending on the framework. For URL parameters in modern APIs, %20 is always safe. + only matters for form-encoded data.
The Uttir URL Encoder defaults to %20 for spaces, with an option to use + for form-encoded data.
Common encoding bugs (and how to avoid them)
URL encoding bugs are among the most common bugs in web development. Here is the short list of what to watch for.
Double-encoding
You have a string with a literal % in it (e.g. "50% off"). You URL-encode it to 50%25 off (correct). Then a downstream service that thinks the input is unencoded runs it through encoding again, producing 50%2525 off (wrong). The receiver gets "50% off" instead of "50% off"... actually, they get the first one right, but they get confused by the double-encoded version.
Fix: agree on a single boundary where encoding happens, and document it. The standard pattern is "the API receives already-encoded URLs, the client is responsible for encoding".
Not encoding reserved characters in query values
The most common bug. The string "a&b" passed as a query value must become a%26b. If you pass a&b directly, the parser sees two parameters.
Fix: always use a URL builder or library that encodes values automatically. Never concatenate strings to build a URL.
Encoding characters that should not be encoded
The opposite mistake. If you encode a path separator (/ becomes %2F) when the path separator is what you meant, the receiving server treats the whole thing as one path segment. This is less common but does happen when an overzealous encoder is applied to entire URLs.
Fix: encode only the values, not the URL structure. Build the URL piece by piece, encoding only the dynamic data.
Mixing up the encoding for the same data
You encode a value as %20, then later the same value is encoded as +, and a downstream consumer expects one or the other. The string "hello world" becomes hello%20world in one place and hello+world in another, and a system that does not normalize between them treats them as different keys.
Fix: pick one encoding (almost always %20) and use it consistently.
Forgetting to encode the hash sign
If a URL value contains a # (the hash sign), it must be encoded as %23. The unencoded # starts a fragment identifier in the URL grammar, so the receiving server never sees the rest of the value. This is the most common cause of "I sent a URL with # in it and the server got an empty value".
Fix: always encode the value, especially before debugging the server to make sure the right value is being sent.
How to encode a URL correctly
The reliable pattern, in any language:
- Build the URL piece by piece: scheme, host, path segments, query parameters.
- Encode each value as you add it. Use the language's built-in URL or URLSearchParams class — never concatenate strings.
- Test with edge cases: spaces, ampersands, Unicode, hash signs.
In JavaScript:
// Good: use URL and URLSearchParams
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'cats & dogs');
console.log(url.toString());
// → https://example.com/search?q=cats+%26+dogs
// Bad: string concatenation
const badUrl = 'https://example.com/search?q=cats & dogs';
// ^^ this breaks the URL
In Python:
from urllib.parse import urlencode, quote
# Good: use urlencode or quote
params = urlencode({'q': 'cats & dogs'})
url = f'https://example.com/search?{params}'
# → https://example.com/search?q=cats+%26+dogs
The pattern is the same in any language. The library handles the encoding; you handle the data.
How to decode a URL
When the server receives a URL, it decodes the percent-encoded characters back to the original bytes. For form-encoded data, the + is also converted to a space.
Most languages do this automatically when you read a URL parameter. JavaScript's URLSearchParams decodes for you. Python's requests library decodes for you. The only time you need to do it manually is when you are reading the raw URL string and parsing it yourself — which is rare.
The Uttir URL Encoder also decodes: paste an encoded URL and it shows you the original.
Common questions
What is the difference between URL encoding and Base64?
URL encoding is for putting data in a URL. Base64 is for representing binary data as ASCII text. They are different. Base64 is sometimes used inside URL-encoded values (e.g. an application/x-www-form-urlencoded body that contains Base64 data), but they solve different problems.
Why are some URLs with spaces still valid?
Most modern browsers will accept a literal space in the URL bar and percent-encode it for you. The URL https://example.com/hello world works in the browser but is technically invalid — the server should treat it as https://example.com/hello%20world. Programmatic clients (curl, libraries) usually do not do this and will reject the URL.
Should I encode the entire URL or just the values?
Just the values. If you encode the structure (the :, /, ?, &, =), the URL no longer parses. The standard pattern is: build the structure, encode the data.
What about IDN (internationalized domain names)?
Domains with non-ASCII characters (e.g. münchen.de) are converted to Punycode (mnchen-3ya.de) for DNS. This is separate from URL encoding — the encoding is at the DNS level, not the URL level. Most modern browsers and libraries handle this automatically.
Bottom line
URL encoding is a small piece of plumbing that is easy to get wrong. The rules are simple (encode the values, not the structure; %20 for spaces, not +; & and = in values are the most common bug), and the consequences of getting it wrong are silent data corruption. The Uttir URL Encoder is the fastest way to encode or decode any string correctly in your browser.