Click a link to a file called “my report.pdf” and the address bar shows something like my%20report.pdf. That %20 is not a bug or a corruption — it is URL encoding doing exactly what it was designed to do: squeezing characters that URLs cannot safely carry into a form they can. Once you understand the handful of rules behind it, every odd-looking percent sign in a link starts to make sense.

Why URLs cannot carry every character

A URL is, at its core, a string of characters sent over a protocol that was designed in an era of plain ASCII text. Spaces, for example, are not allowed in a URL at all — a space would be ambiguous, since spaces separate tokens in many protocols. Other characters are allowed but carry special meaning: the question mark starts the query string, the ampersand separates parameters, the hash starts a fragment, and the slash separates path segments.

URL encoding, formally called percent-encoding, solves both problems with one mechanism. Any byte that cannot appear directly is replaced by a percent sign followed by two hexadecimal digits representing that byte. A space is byte 0x20, so it becomes %20. An ampersand is 0x26, so it becomes %26. The original character is recoverable by reversing the process, which is called decoding.

Reserved, unreserved, and everything else

Not every character needs encoding. The standard divides characters into two sets:

The reserved-set distinction is the key insight. A slash in a path is structure and stays as-is; a slash inside a search term is data and must become %2F, or the server will read it as a path separator and route your request to the wrong place.

How non-ASCII characters get encoded

Percent-encoding works on bytes, not characters. To encode a character outside ASCII, you first convert it to its UTF-8 bytes, then encode each byte. The letter é, for example, is two UTF-8 bytes (0xC3 0xA9), so it becomes %C3%A9. That is why a search for "café" produces a URL containing caf%C3%A9 — each percent triplet is one byte, not one character.

This is also why encoded URLs look longer than the original text: a single emoji can expand to four percent triplets, twelve characters in the address bar. Modern browsers hide most of this by displaying the decoded characters in the address bar while sending the encoded form under the hood, which is why a pasted link can look clean in one place and encoded in another.

The %20 versus + trap

The most common encoding confusion involves the plus sign. In the query string of a URL, a + is traditionally interpreted as a space, a leftover from HTML form encoding. So a search for "peanut butter" may appear as q=peanut+butter in one system and q=peanut%20butter in another, and both usually work.

But the plus-as-space rule applies only to query strings (and form data). In a path segment, a + is a literal plus sign. Encoding a path with + for spaces, or decoding a query with a decoder that treats + literally, produces wrong results in both directions. The safe rule: use %20 for spaces everywhere, and only treat + as a space when you are explicitly parsing form-encoded query data.

Worked example: building a search URL by hand

Say you want to link to a search for "fish & chips" on a recipe site. Start with the base: https://example.com/search?q=. Then encode the query value: the space becomes %20, and the ampersand — which would otherwise be read as the start of a new parameter — becomes %26. The final URL is https://example.com/search?q=fish%20%26%20chips.

Skip the encoding and you get https://example.com/search?q=fish & chips, which breaks in two ways: the space is invalid, and the server reads a parameter named " chips" with no value instead of part of your search term. This is exactly the bug that appears when links are built by gluing strings together instead of encoding each value as it is added.

Double encoding and other traps

Encoding a value that is already encoded produces double encoding, one of the most confusing bugs in web development. Encode %20 once more and the percent sign itself becomes %25, giving %2520 — which decodes to the literal text %20, not to a space. The symptom is a link that looks almost right but lands on a "not found" page, and the fix is always the same: encode each value exactly once, at the point where it enters the URL.

Two more traps worth knowing: never encode an entire URL in one pass, or you will encode the slashes and colons that hold it together — encode each component (path segment, query value) separately. And beware of case: %2f and %2F are equivalent, but some systems compare URLs as strings, so inconsistent casing can defeat caching and duplicate detection. Uppercase hex digits are the convention; stick to them.

AdvertisementAd slot: in-content

Frequently asked questions

What does %20 mean in a URL?

It is a space. Percent-encoding represents a byte as a percent sign followed by two hexadecimal digits, and the space character is byte 0x20, so it becomes %20. Browsers often display the decoded space in the address bar, but the encoded form is what actually travels over the network.

Is URL encoding the same as encryption?

No. Encoding is fully reversible and is not meant to hide anything — anyone can decode a URL instantly. Its purpose is transport safety: making sure characters that would break URL syntax arrive intact. Never rely on encoding to protect sensitive data; that is what encryption and proper access control are for.

Should I encode a whole URL at once?

No. Encode each component separately — each path segment and each query value — and leave the structural characters (slashes, question marks, ampersands that separate parameters) untouched. Encoding a whole URL mangles its structure and produces a link that points nowhere.

Why do I sometimes see %2F instead of a slash?

That is an encoded slash appearing as data rather than as a path separator. It shows up when a value containing a slash, such as a date or a nested path, is placed inside a query parameter or a single path segment. Decoding it gives back the slash; leaving it encoded keeps the server from splitting the value into separate segments.