URL Decoder
Decode percent-encoded URLs and break query strings into a readable table.
URL Decoder tool
What this tool does
It turns %C3%A9 back into é, %2F back into
/, and a 400-character analytics URL back into something you can read. Paste a
whole address and it also splits out the scheme, host, path and fragment, and lists every
query parameter as a row you can actually scan.
Multi-byte characters are reassembled properly. A single emoji arrives as four separate escapes, and decoding them one at a time produces four pieces of nonsense — so consecutive escapes are collected and decoded together.
Common uses
- Reading an OAuth callback or SSO redirect, where the interesting value is a nested URL wrapped in escapes.
- Working out what a tracking link is actually pointing at before clicking it.
- Debugging a request where a parameter arrived wrong — seeing the raw and decoded forms side by side usually identifies whether the sender or the receiver is at fault.
- Pulling a signed URL apart to inspect its expiry and signature parameters.
- Recovering a filename from a
Content-Dispositionheader.
A short example
This value:
next=https%3A%2F%2Fapp.example.com%2Fbilling%3Fplan%3Dpro decodes to:
next=https://app.example.com/billing?plan=pro
Note that the inner ? was encoded. Had it not been, the outer URL would have
treated plan=pro as one of its own parameters.
Double encoding, and how to recognise it
If decoding leaves %3A still visible in the output, the value was encoded twice.
The giveaway is %25 in the input: that is an encoded percent sign, which means
the escapes after it are themselves escaped. Turning on repeated decoding unwinds the whole
chain and reports how many passes it took.
This matters beyond tidiness. A proxy or gateway that decodes once while the application
decodes twice is the basis of several path-traversal bypasses — %252e%252e%252f
passes a filter looking for ../ and becomes it a layer later.
Worth knowing
A decode can legitimately fail. %ZZ is not an escape, and %E9 alone
is a Latin-1 byte from the old escape() function, not valid UTF-8. Rather than
silently producing replacement characters, this tool names the position that failed and
explains what the sequence probably was.
One caution about the parameter table: duplicate keys are kept, because they are meaningful.
?tag=a&tag=b is two values, and frameworks disagree about whether that means
a list, the first value, or the last one.
Frequently asked questions
Why does a plus sign sometimes become a space and sometimes not?
It depends which decoder runs. decodeURIComponent("a+b") hands back a+b untouched, while a form decoder — URLSearchParams, PHP’s $_GET, most framework query parsers — returns a b, because a plus means a space in application/x-www-form-urlencoded. That is what quietly destroys Base64 signatures passed in query strings, where the plus was literal and should have been sent as %2B.
Should I decode a query string before or after splitting it?
After, always. Split on &, then on the first =, and decode each name and value only at the end. Decode first and an escaped %26 inside a value becomes a real separator, so note=a%26b=c turns into two parameters and part of the text vanishes. That ordering mistake is the basis of most parameter-smuggling reports.
Where does everything after the hash go?
Nowhere — a fragment is never transmitted. RFC 3986 §3.5 makes it purely client-side, so the server, the access log and any WAF in between see the URL up to but not including the #. That is why OAuth implicit-flow tokens placed in a fragment stay out of server logs, and why a redirect target hidden after a hash cannot be validated on the server.