When you paste a search query into a browser, the address bar often fills with things like
%20, %C3%A9, or q%3Dhello. That is URL encoding, also called
percent-encoding. This guide explains what those codes mean, why they appear, and how the encoding is
computed step by step. The diagrams below run the real algorithm in your browser, so you can type your
own text and watch it get encoded.
What URL encoding actually is
URL encoding is a way to put characters into a URL that the URL syntax does not allow as-is. A URL is defined over a limited set of bytes. Anything outside that set, and a handful of characters that have special meaning inside a URL, must be rewritten so the URL stays valid and unambiguous.
The rewrite is simple to describe: take the byte, convert it to two uppercase hexadecimal digits, and put
a percent sign in front. The space byte 0x20 becomes %20. The letter
A (byte 0x41) would become %41, but because A is
already allowed, it stays A.
One detail trips people up: URL encoding is not encryption and not compression. The original text is fully recoverable, and the result is usually longer than the input. It is a transport format, not a way to hide data.
Why URLs need encoding
A URL has structure. Characters such as ?, &, =,
/, and # separate the path, query, and fragment. If the data you want to send
contains one of those characters, the parser cannot tell whether it is part of your data or part of the
URL structure. Encoding removes that ambiguity by turning the structural characters into inert
%XX sequences.
Encoding is also needed for characters that are not even legal in a URL at all, such as spaces, control characters, and most non-ASCII text. A raw space breaks the URL at the space, and a raw accented letter is not a valid byte in the ASCII URL grammar.
The core rule: encode the bytes, not the characters
The single most useful thing to understand is that URL encoding operates on bytes. The
text is first turned into a sequence of bytes using a character encoding (almost always UTF-8), and then
each byte that is not allowed becomes %XX. This matters most for non-English text, as the
next sections show. For plain ASCII, the byte and the character are the same thing, so the difference is
invisible.
Reserved versus unreserved characters
The set of characters that stay untouched is small and fixed by RFC 3986. It is called the unreserved
set: the uppercase and lowercase letters A–Z, the digits
0–9, and four symbols: hyphen -, period .,
underscore _, and tilde ~. Everything else printable, plus every control
character, gets percent-encoded.
Click around the grid to see which characters survive and which turn into codes:
Click any character to see whether it stays as-is or becomes percent-encoded. Green = unreserved (kept); red = encoded.
Walk through a full encoding
Type a string below and press play. The diagram encodes one character at a time, shows the UTF-8 byte or
bytes for that character, and builds up the final percent-encoded string. Try hello world,
then try something with punctuation.
Type something or keep the example. Press play to encode each character, one byte at a time, using real UTF-8.
Spaces: %20 in a URL, + in form data
Spaces are the most common source of confusion because there are two conventions. Inside
a URL path or query string, a space is encoded as %20. But in
application/x-www-form-urlencoded data, which is what a browser sends when a form is
submitted with the default encoding, a space becomes a literal plus sign +.
Both forms are valid in their own context. The bug appears when they are mixed: a server that decodes
form data expects + to mean space, while a server decoding a query string expects
%20. If you send + into a query-string decoder, it may stay as a plus, and if
you send %20 into a form decoder, it works but looks unusual.
Step through the same text encoded both ways to see exactly where they diverge:
The same text is encoded two ways. Press play to step through each character and spot where the encodings differ.
| Char | In a URL / query (encodeURIComponent) | In form data (x-www-form-urlencoded) |
|---|
Non-ASCII text and UTF-8
URL encoding knows nothing about characters such as é, 中文, or emoji. It only
knows bytes. So before encoding, the text is converted to UTF-8. An ASCII character like
c is one byte (0x63) and becomes one code (%63). The letter
é is two UTF-8 bytes (0xC3 0xA9) and becomes two codes
(%C3%A9). A Chinese character is three bytes, so it becomes three codes.
This is why a short non-English word can produce a long encoded string, and why the encoding is reproducible only if both sides agree on UTF-8. If the sender used a different legacy encoding, the bytes would differ and the receiver would decode garbage.
URL encoding works on bytes, not characters. Non-ASCII code points are expanded to UTF-8 bytes first. Try the examples or type your own.
URL encoding versus HTML entities and Base64
Three encodings get mixed up because they all turn "weird" text into "safe" text.
- URL encoding targets URL syntax. It produces
%XXand is meant for URLs and form fields. - HTML entities target HTML markup. They produce
&oréso a character shows up correctly inside an HTML document. The same character can look very different in the two systems. Our HTML entities guide explains the<and&rules in depth. - Base64 targets binary data. It produces a blob of letters, digits,
+,/, and=that is safe to drop into text, including a URL, but it is meant for arbitrary bytes, not for fixing URL syntax. Our Base64 guide covers it in depth.
A common pattern is to Base64-encode binary data and then URL-encode the Base64 result so it can sit in a query parameter. The two encodings stack; they are not alternatives to each other.
When you actually need it
You rarely call a URL encoder by hand. The platform does it for you:
-
In JavaScript,
encodeURIComponent()encodes a query value, andencodeURI()encodes a whole URL while leaving the structural characters alone. They are not interchangeable. -
In a browser, setting
form.enctypeto the defaultapplication/x-www-form-urlencodedmakes the browser encode fields for you. - On the server, every framework exposes a function to read query strings and form bodies back into normal strings.
You still need to understand it when the automatic encoding is missing, when you build URLs by string
concatenation, or when data round-trips through more than one system. The usual failure is a value that
contained & or = and was never encoded, so the receiver split it into extra
parameters.
Common mistakes
- Encoding the whole URL with
encodeURIComponentdestroys the?,&, and=that structure it. Encode the values, then assemble the URL. - Doubling the encoding. Encoding an already-encoded string turns
%20into%2520, which decodes to a literal%20instead of a space. - Mixing space conventions. Sending
+where%20is expected leaves a plus in the data. - Forgetting the character encoding. The same text encodes differently under UTF-8 versus a legacy encoding, so agree on UTF-8 on both ends.
Recommended reading
You can put this into practice with the URL Encoder / Decoder, or see how a full address breaks apart with the URL Parser. For the surrounding concepts, read our web encoding guide, URL structure guide, and query string parsing guide. The Base64 guide covers the sibling encoding used for binary data.