URL Decoder
Turn percent-encoded text back into something you can read — and find out why it broke.
Encoded
Decoded
Decoding is easy until it is not
Most of the time you paste a URL from a log, hit decode, and read what someone actually searched for. The interesting cases are the other ones: the value that decodes to something with a stray %20 still in it, the query string whose spaces come back as plus signs, and the one that just refuses with URI malformed and no hint as to where.
That last one is worth dwelling on. The browser's decodeURIComponent throws the same URIError: URI malformed for four genuinely different faults — a % with nothing after it, a %zz that is not hex, a multi-byte sequence that was cut short, and bytes that decode but are not valid UTF-8. It tells you nothing about which, or where. In a 3KB query string that is not a useful answer, so this page decodes by hand and points at the character.
The + question has no universal answer, which is why it is a switch rather than a guess. In application/x-www-form-urlencoded — what a submitted form sends, and what most query strings in the wild are — a + means a space. Everywhere else it means a plus sign, and decodeURIComponent leaves it alone. Pick the reading that matches where the text came from; the note under the output always says which one was applied.
Everything happens in the page. Query strings carry session tokens and personal data more often than people expect, so nothing you paste here leaves your browser.
Decoding a value
- Paste the encoded text – A whole URL, a single parameter value, or a query string — all three work. You do not need to strip the
?or split the parameters first. - Decide what a + means here – If the text came out of a form submission, a browser address bar, or an access log, choose A space. If it came from code that used
encodeURIComponent, choose A plus sign. When there is no+in the input the setting makes no difference and the note says so. - Read the note under the output – It reports what the shape of your input tells you — whether it held escaped plus signs, whether it contained multi-byte characters, whether there was anything to decode at all. None of these are problems; they are the things that explain a surprising result.
- Take the amber warning seriously – If a sequence like
%2520shows up, the value was encoded twice somewhere upstream. Decoding once gives you the literal text%20rather than a space. That is almost always a bug in the pipeline rather than in the data. - Follow the error to the character – When a sequence is broken the message names the position and shows the offending run. The usual cause is a URL that was truncated mid-escape by a log line limit or a chat client.
If the decoded text still has % sequences in it, do not just decode again. That works, and it hides the actual bug — something in the chain is encoding a value it should have passed through. Fix the layer, not the symptom.
The + that costs an afternoon
Here is the same query string read both ways. Nothing errors in either case, which is exactly why this one is hard to spot — you get a plausible-looking answer and a subtly wrong value.
q=SIM+swap&msisdn=%2B447700900112 # a browser built this from a form, # so the space is a + and the real # plus was escaped to %2B
# + read as a space — correct here q=SIM swap&msisdn=+447700900112 # + read as a plus — what # decodeURIComponent gives you q=SIM+swap&msisdn=+447700900112 # no error either way. the second # one just has a wrong q value.
When you need this
Reading a URL out of a log file
Access logs store the request line encoded, so a search for SIM swap — 2nd attempt is written SIM%20swap%20%E2%80%94%202nd%20attempt. Decoding it is the difference between skimming a log and grepping it.
Working out what a redirect actually points at
OAuth and SSO flows carry a whole URL inside a redirect_uri parameter, which means its ? and & are escaped. Decoding tells you where a login is really going to send the user — a check worth doing on any callback URL you did not write yourself.
Reproducing a failing request
Copy the URL out of the browser's network tab, decode it, and you can read the parameters the front end sent rather than guessing. Pair it with the query string to JSON page and you get them as a structured object you can diff against what the API expected.
Chasing a mojibake bug
When a name arrives as Linnéa, the bytes were UTF-8 but something read them as Latin-1. Decoding the raw escapes here shows you the actual bytes — %C3%A9 is a correctly encoded é, while %C3%83%C2%A9 is that sequence encoded a second time by a mis-configured layer. The two look identical after a careless decode.
Checking a value that will not match
A token that fails to validate, an ID that returns 404, a signature that will not verify — it is worth decoding the parameter before assuming the value is wrong. More than once the answer has been a trailing %0A from a copy-paste that grabbed the newline.
What this page does
- Points at the exact character when a sequence is broken, and says which of the four faults it is — where
decodeURIComponentgives one message and no position. - An explicit switch for the
+reading, with a note saying which one was applied, so a surprising result explains itself. - Flags double-encoded sequences such as
%2520rather than quietly decoding one layer and moving on. - Strict UTF-8 validation that rejects overlong forms and encoded surrogates — sequences that some decoders accept and that have been used to slip past filters.
- Reports the decoded byte count, which is what a length limit on the far end is actually counting.
- Runs entirely in your browser, which matters more here than usual — query strings carry tokens.
Questions people actually ask
Why are my spaces coming back as plus signs?
Because the text is form-encoded and the decoder is reading + literally. Switch the setting to A space. This catches people out because decodeURIComponent — the function most code reaches for — does not do it: decodeURIComponent('a+b') returns a+b, not a b. If you are decoding query strings in code, use URLSearchParams instead, which handles it.
What does URI malformed mean?
It means the input is not a valid percent-encoded string, and the browser will not tell you more than that. There are four causes: a % at the end with nothing after it, a % followed by non-hex characters, a multi-byte UTF-8 sequence that is missing its continuation bytes, and bytes that are well-formed escapes but not valid UTF-8. This page distinguishes them and gives you a position.
My value decoded but still has %20 in it. What now?
It was encoded twice. Somewhere upstream a layer encoded a value that had already been encoded, so the original % became %25. Decoding once correctly gives you back the intermediate form. Decoding again gets you the text you want but leaves the real bug in place — see OWASP on double encoding, which also covers why it is a security concern and not only a nuisance.
Is %2F in a path safe to decode?
Safe to read, yes — but be careful about what you conclude. Several servers and proxies deliberately reject or normalise an encoded slash in a path before your application ever sees it, precisely because decoding it changes the path structure. If a URL with %2F behaves differently on two environments, that difference is usually the reason.
Why does this reject sequences my other decoder accepts?
Because it validates UTF-8 strictly. %C0%80 is an overlong encoding of a null byte and %ED%A0%80 is an encoded surrogate — both are illegal under RFC 3629, and both have historically been used to slip content past filters that only checked the decoded form. A decoder that accepts them is not being helpful.
Can I decode a whole URL at once?
Yes, and it is usually what you want for reading. Just remember the result is for human eyes: a URL with its %3F and %26 turned back into ? and & is no longer the same URL, and pasting it into a browser will not do the same thing. If you want the parts separated properly rather than flattened, the URL parser does that instead.
Related tools
Specifications and references
- WHATWG URL Standard — urlencoded parsing – The algorithm browsers use, including the plus-sign rule
- RFC 3986 §2.4 — When to encode and decode – Why decoding early is a mistake, stated by the spec itself
- MDN — decodeURIComponent – Including the error it throws and what triggers it