URL encoder and decoder
Percent-encode a value for a query string, or decode one you have been handed. One tick box switches between encoding a single component and a whole address.
Two different jobs, one tick box
Percent-encoding replaces characters that mean something structural in a URL with a
% and their byte value. Which characters need replacing depends entirely on whether you are
encoding a whole URL or a single value inside one. Getting this backwards is the most
common URL bug there is.
| Component (default) | Whole URL (ticked) | |
|---|---|---|
| Use for | One parameter value, one path segment | A complete address |
Encodes / ? : @ & = + $ # | Yes | No |
| Encodes spaces and non-ASCII | Yes | Yes |
| JavaScript equivalent | encodeURIComponent | encodeURI |
Leave the box unticked when you are encoding something that goes into a URL — a search term, a redirect target, a filename. Tick it when you have an entire address containing spaces or accented characters and you want the structure left intact.
Why it matters, with an example
You are building a redirect link and the destination is itself a URL with parameters.
https://acme.test/go?to=https://other.test/p?id=9&ref=mail
The server reads three parameters on your URL: to, id and ref. The
destination has been torn apart.
https://acme.test/go?to=https%3A%2F%2Fother.test%2Fp%3Fid%3D9%26ref%3Dmail
Now there is one parameter whose value happens to be a URL, and it survives intact. This is the case that breaks analytics links, OAuth callbacks and email tracking every day.
Plus signs and spaces
A space can appear in a URL two ways. As %20, which is correct anywhere. Or as +,
which means a space only inside a query string, and only because HTML form submission
(application/x-www-form-urlencoded) defined it that way in the 1990s.
In a path, + is a literal plus sign. So /files/report+final.pdf is a file with a
plus in its name, while ?q=report+final is a search for "report final".
Tick Read + as a space when decoding a form submission or a query string. Leave it off
when decoding a path, or you will corrupt any genuine plus signs — which matters most with email addresses
using the user+tag@example.com convention.
Reserved and unreserved characters
RFC 3986 splits URL characters into two groups. Unreserved characters —
A–Z a–z 0–9 - . _ ~ — are never encoded and never need to be. Everything else is either
reserved, meaning it has a structural job, or outside the allowed set entirely.
| Character | Encoded | Structural meaning |
|---|---|---|
| space | %20 | Not allowed in a URL at all |
| & | %26 | Separates query parameters |
| = | %3D | Separates a parameter from its value |
| ? | %3F | Starts the query string |
| # | %23 | Starts the fragment — never sent to the server |
| / | %2F | Separates path segments |
| + | %2B | Means a space in a query string |
| % | %25 | Starts an escape sequence |
Non-ASCII characters are encoded as their UTF-8 bytes, so é becomes %C3%A9 —
two bytes, two escapes. That is why an encoded Japanese or Arabic URL looks so much longer than the original.
Doing it in code
encodeURIComponent("linen shirt & scarf") // linen%20shirt%20%26%20scarf
encodeURI("https://a.test/my file.pdf") // https://a.test/my%20file.pdf
// building a query string properly
const qs = new URLSearchParams({{ q: "linen shirt", sort: "price asc" }});
`/search?${{qs}}` // /search?q=linen+shirt&sort=price+asc
from urllib.parse import quote, quote_plus, urlencode
quote("linen shirt") # linen%20shirt
quote_plus("linen shirt") # linen+shirt
urlencode({{"q": "linen shirt", "sort": "price asc"}})
In both languages, building the query string with the provided helper rather than by concatenating strings
removes this whole category of bug. Use URLSearchParams or urlencode and you will
never have to think about it again.
Questions about this tool
Which mode should I use?
If you are encoding one value that will sit inside a URL — a search term, a redirect target, a filename — leave the box unticked. If you have a whole address that contains spaces or accented characters and you want :// and ? left alone, tick it.
Why does decoding sometimes fail?
Because a % is not followed by two valid hex digits. That usually means the text was double-encoded, or a genuine percent sign was never escaped. Try decoding twice — if the first pass produces something still full of %25, it was double-encoded.
What is double encoding and why is it a problem?
Encoding something that was already encoded. %20 becomes %2520, and the receiving end decodes it once and gets %20 as literal text rather than a space. It happens when a value passes through two layers that each helpfully encode it. Decode until the output stops changing to see how many layers deep you are.
Does the fragment after # get sent to the server?
No. Everything after # is handled by the browser and never leaves it. That is worth knowing when you are debugging why a server cannot see part of a URL.
Related tools
Learn the why, not just the how
Longer reading on formatting, indentation and minification.