Skip to content
🚀 10 tools live, free forever — the toolbox keeps growing
Home›Blog›URL Encoding Explained: Percent-Encoding, %20 and Common Bugs
Guides

URL Encoding Explained: Percent-Encoding, %20 and Common Bugs

How a character becomes %XX, which characters a URL lets you use as they are, why encodeURIComponent and encodeURI give different answers, when a space is + and when it is %20, and how to track down the double encoding behind a %2520.

✍ By the Founder & CEO, Team BuildToolForge🗓 2026-10-31⏱ 8 min
url-encodingpercent-encodingquery-stringutf-8encodeuricomponent
Encoding Guide2026

URL Encoding
Made Simple

How text travels safely inside a URL

Reserved characters · %20 vs + · UTF-8

url · encode
> encodeURIComponent("café & crème")
outcaf%C3%A9%20%26%20cr%C3%A8me
> decodeURIComponent("a%2520b")
outa%20b
✓ é is two UTF-8 bytes, so it becomes two %XX
Common encodings
%20
space
%2F
/
%3F
?
%26
&
%3D
=
%25
%
Unreserved: never encodedReserved: encoded inside valuesUTF-8 bytesNot encryption

What URL encoding actually is

URL encoding, also called percent-encoding, is how a URL carries characters it is not allowed to contain. A URL may only use a small set of ASCII letters, digits and punctuation. Everything else — a space, an accented letter, an emoji, a Cyrillic or Arabic word — has to be written as a % followed by two hexadecimal digits for each byte.

The same trick is used for characters that are allowed but mean something. A ? starts the query string and an & separates parameters, so a search for fish & chips has to send the ampersand as %26, or the server sees two parameters instead of one.

That is the whole job: make a value safe to put inside a URL without changing what the URL means. It is not compression, not encryption and not a security filter.

How a character becomes %XX

Encoding works on bytes, not characters. The text is first turned into UTF-8 bytes, then every byte that needs encoding is written as % and its value in hex.

A plain ASCII character is one byte, so a space (byte 32, hex 20) becomes %20. Characters outside ASCII take two to four bytes in UTF-8, and each byte gets its own escape:

  • é → bytes C3 A9 → %C3%A9
  • € → bytes E2 82 AC → %E2%82%AC
  • 😀 → bytes F0 9F 98 80 → %F0%9F%98%80

Decoding reverses the steps: collect the bytes, then read them as UTF-8. If the bytes were produced with a different character set, such as Latin-1, the result comes out as mojibake — café instead of café. Every modern browser and framework uses UTF-8, and you should too.

Reserved, unreserved and why context matters

RFC 3986 splits URL characters into two groups.

Unreserved characters — A–Z a–z 0–9 - . _ ~ — never need encoding. Encoding them is legal but pointless, and a good encoder leaves them alone.

Reserved characters — : / ? # [ ] @ ! $ & ' ( ) * + , ; = — give a URL its structure. Whether you encode one depends on where it is. The / between path segments must stay a slash; a / inside a file name you are passing as a parameter must become %2F. That is why there is no single right way to "encode a URL": you encode each piece for the place it is going.

encodeURIComponent vs encodeURI

JavaScript has two built-in functions and picking the wrong one is the most common URL bug there is.

FunctionUse it forLeaves alone
encodeURIComponentOne value: a parameter, a path segmentOnly unreserved characters
encodeURIA whole address you want to keep workingReserved characters too

Run encodeURI on a value like a&b=c and the & and = survive, so your one parameter becomes two. Run encodeURIComponent on a whole URL and every / and : is escaped, so the link stops being a link. The rule of thumb: build URLs from pieces and encode each value with encodeURIComponent — or better, let URLSearchParams do it for you.

Other languages have the same split: Python's quote and quote_plus, PHP's rawurlencode and urlencode. Our URL encoder and decoder offers both modes so you can see the difference on your own input.

+ or %20: the form-encoding exception

In a path, a space is %20 and a + is just a plus sign. But HTML forms submitted with the default application/x-www-form-urlencoded type encode a space as + in the query string, and servers decode it back.

The trouble starts when the two conventions meet. A phone number like +44 20 7946 0000 put into a query string without encoding arrives as 44 20 7946 0000, because the server read the + as a space. The fix is to encode the plus as %2B, which every encoder that follows the form rules does. When you are unsure which convention the other side expects, %20 for a space and %2B for a plus are always safe.

Double encoding and the %2520 bug

%25 is the encoded form of the percent sign itself. So when you see %2520 in a URL, it is %20 that was encoded a second time: a value went through an encoder after it had already been encoded.

It usually happens at a boundary — a framework encodes a parameter that the calling code had already encoded, or a redirect URL is encoded once to fit inside another URL and then again by a library. The visible symptom is a 404, a search for the literal text %20, or a filename full of percent signs.

Decoding twice recovers the original, but the real fix is to find the second encoder and remove it. Encode exactly once, at the moment a value is placed into a URL.

Encoding is not escaping, and not encryption

Percent-encoding makes a value safe inside a URL. It does nothing for HTML, JavaScript or SQL. Once the server decodes %3Cscript%3E it has a perfectly ordinary <script> again, so output still has to be escaped for wherever it ends up, and database queries still need parameters.

It is not secrecy either. Anyone can decode a URL, and URLs are written to server logs, browser history, analytics tools and the Referer header of the next page. Never put a password, token or personal data in a query string because it "looks scrambled". The same is true of Base64, as our Base64 guide explains.

When decoding goes wrong

Most URL problems are one of these:

  • "URIError: malformed URI sequence". A % is not followed by two hex digits, or the bytes are not valid UTF-8. A literal percent sign must itself be sent as %25.
  • Accents come out as é or �. The bytes were written in one character set and read in another. Make sure both sides use UTF-8.
  • A parameter is cut short at an &. The value was not encoded, or was encoded with encodeURI instead of encodeURIComponent.
  • Plus signs turn into spaces. Form decoding treated + as a space. Encode a real plus as %2B.
  • %2520, %253D and friends. Double encoding; find the second encoder.

Paste the URL into a decoder and look at each query parameter separately — the breakdown usually makes the problem obvious within seconds.

Related tools and guides:

Frequently Asked Questions

What is URL encoding?⌄

URL encoding, also called percent-encoding, writes characters that are not allowed in a URL as a % followed by two hexadecimal digits for each UTF-8 byte. A space becomes %20 and é becomes %C3%A9. It lets any text travel inside a URL without changing its meaning.

Why is a space sometimes + and sometimes %20?⌄

HTML forms submitted as application/x-www-form-urlencoded encode a space as + in the query string. Everywhere else, including paths and most APIs, a space is %20 and + is a literal plus. %20 is always safe.

Should I use encodeURI or encodeURIComponent?⌄

Use encodeURIComponent for a single value such as a query parameter or path segment, because it encodes &, =, / and ?. Use encodeURI only for a complete URL that must keep its structure.

What does %2520 mean?⌄

It is a double-encoded space: %25 is the percent sign, so %2520 is %20 encoded a second time. Decode it twice to read it, then fix the code that encodes the value twice.

Is URL encoding secure?⌄

No. It is reversible by anyone and offers no secrecy, and decoded input can still carry an injection attack. Escape output for HTML and use parameterised queries for databases; never put secrets in a URL.

About the author
Founder & CEO, BuildToolForge
Follow on X: @Buildtoolforge

15+ years of hands-on experience in web development and security. Focused on practical web security: keeping websites secure, reliable and easy to troubleshoot. BuildToolForge exists to make these checks free, private and understandable. Tools run in your browser wherever possible, and nothing you enter into a tool is stored.

How we write our guides: Every guide is written from hands-on experience, checked against the official standards, and tested with our own tools on real domains. We update guides when standards or tools change. Found a mistake? Email contact@buildtoolforge.com.

Last updated: November 2, 2026