URL Decode

Decode percent-encoded URLs and query strings back to readable text in your browser with decodeURIComponent or decodeURI. Nothing leaves your device.

🌐 Español

🔒 Private by design: your text is processed locally in your browser and never uploaded to any server.

Reading a logged redirect URL back to plain text

The version of a URL that ends up in a server log, an analytics export or a support ticket is rarely the version a human can read. Somewhere along the way a whole URL got stuffed into another URL’s query string, and every character that would have confused the parser was rewritten as a percent sign followed by two hexadecimal digits.

What you are usually looking at is a redirect chain. A click on a marketing email hits a tracking domain, which carries the real destination as a parameter, which itself carries a campaign string, which may carry a return path. Each nesting level escapes the level below it, and by the third one you are reading %253A%252F%252F and guessing.

Percent-encoding is a byte-level scheme, not a character-level one. Each escape names one byte of the UTF-8 representation, which is why a single accented letter shows up as two escapes and an emoji as four. Working that back by eye is not realistic, and it is exactly what the browser’s built-in decoders exist for.

Decoding a pasted URL or query string

  1. Paste the encoded text into the box above, which shows the placeholder Paste your text here….
  2. Set Decoding scope. Leave it on Component (decodes everything, for query params & path segments) for a single parameter value, or switch to Full URL (leaves encoded /, :, ?, & untouched) for a complete address.
  3. Click URL Decode. The button is greyed out while the box is empty and reads Working… during the run.
  4. Read the result in the read-only box, then click Copy to clipboard to take it, or Process another to start over with something else.

decodeURI leaves the reserved punctuation alone

The two scopes are not “thorough” and “less thorough”. They are two functions with different jobs, and picking the wrong one produces a plausible-looking answer that is wrong.

Component scope runs decodeURIComponent, which turns every valid escape back into its character with no exceptions. That is correct when what you hold is a single value: one query parameter, one path segment, one form field. Inside a value, a %2F was only ever a literal slash somebody wanted to store, so decoding it back to / restores the truth.

Full URL scope runs decodeURI, which refuses to decode escapes for the punctuation that gives a URL its structure. Encoded forms of /, ?, &, #, :, ;, =, ,, @, $ and + are all preserved as escapes. The reasoning is that if you hand it a whole address, turning an encoded slash inside a parameter into a real slash would silently change which path the URL points at. Both functions decode spaces and non-ASCII characters identically.

The practical rule: if you copied a whole address including the scheme, use Full URL. If you copied the bit after an equals sign, use Component.

A plus sign is not a space

This trips up more people than any other detail on the page. In application/x-www-form-urlencoded, the format HTML forms use for GET query strings and classic POST bodies, a space is written as +. In URI percent-encoding, a space is written as %20. They are different specifications that happen to share a syntax.

decodeURIComponent implements the second one only. Paste first+name=Ada+Lovelace and you get it back unchanged, plus signs and all, because as far as the URI grammar is concerned a plus is an ordinary reserved character with no special meaning inside a value.

There is a nastier corner to this. A form encoder writes a genuine plus sign as %2B, so after decoding, a real plus and a plus that meant a space look identical. If you are cleaning up form data by hand, replace plus signs with spaces before you decode, not after.

Double-encoded values need two passes

A URL that has passed through two systems that each escaped it carries two layers. The tell is %25, the escape for the percent character itself: %253A is %3A with the percent escaped again, and %3A is a colon.

One run peels one layer. To go further, copy the result with Copy to clipboard, click Process another, paste it back in and run it again. That is genuinely how the tool works, since the output box is read-only and starting a new run clears both boxes rather than feeding one into the other.

Stop when the output has no percent signs left, or when it stops changing. Decoding once more than necessary is not harmless: it can turn escapes that were meant to be literal text into structural punctuation, which is one of the classic ways a URL parser can be tricked.

Malformed percent sequences and the message you see

Both built-in functions are strict. A percent sign must be followed by exactly two hexadecimal digits, and multi-byte sequences must form valid UTF-8. A lone %, a sequence like %zz, or a truncated escape such as the first two bytes of a three-byte character all throw rather than guessing.

What reaches the page is the shell’s general failure message asking you to check the input and try again. The precise diagnosis is logged to the browser console instead, which is worth knowing when you are staring at a long string and wondering which end of it is broken. Trimming a trailing partial escape from a copied log line fixes it more often than not.

For the opposite operation, URL Encode percent-encodes text with the same two scopes. If the value you decoded turns out to be a token, JWT Decode reads its payload, and HTML Decode handles entities such as & rather than percent escapes. More utilities are on the developer tools hub and in the developer tools guide.

See it in action

Screenshot of the URL Decode tool with the sample input “https%3A%2F%2Fsysfenix.com%2Fsearch%3Fq%3Dconver…”, Decoding scope set to Component (decodes everything, for query params & path segments)
URL Decode mid-process: the sample input “https%3A%2F%2Fsysfenix.com%2Fsearch%3Fq%3Dconver…”, Decoding scope set to Component (decodes everything, for query params & path segments).
Screenshot of the URL Decode result screen showing the generated output “https://sysfenix.com/search?q=convert png to jpg…”
The finished result: the generated output “https://sysfenix.com/search?q=convert png to jpg…”. The download link is a local blob URL — the file never leaves your device.

Frequently asked questions

My decoded text still has plus signs where spaces should be. Why?

Because decodeURIComponent has no idea about form encoding. HTML forms submit spaces as a plus sign, a convention that predates the modern URI rules and is not part of the URI syntax, so the built-in decoder leaves it exactly as it found it. Replace the plus signs with spaces before you decode rather than after, because a genuine plus travels as %2B and becomes indistinguishable from a plus that meant a space once the pass has run.

The output still contains %XX sequences. Did it fail?

Probably not. Two things produce that. On Full URL scope, decodeURI deliberately refuses to touch escapes for reserved punctuation, so an encoded slash or question mark stays encoded on purpose. And a value that was encoded twice needs two passes, because one pass only strips the outer layer.

What does a malformed percent sequence actually do?

The run stops and the box shows the shell's standard failure message asking you to check the input. The specific reason, naming the invalid or incomplete percent-encoding, is written to your browser's developer console rather than to the page. The usual culprits are a stray percent sign followed by something that is not two hex digits, and a multi-byte UTF-8 escape that got truncated.

Can I see the input and the output at the same time?

No. When the decode finishes, the input box is replaced by a read-only box holding the result, so there is one view at a time. To change the input you click Process another, which clears both boxes and returns you to an empty textarea.

Does the choice of scope change how non-ASCII characters decode?

Not for the characters themselves. Both functions turn a valid multi-byte UTF-8 escape sequence back into the accented letter, emoji or non-Latin character it represents. The scope only governs the reserved punctuation of URL syntax, which decodeURI holds back and decodeURIComponent does not.

Is anything about my pasted text kept after I close the tab?

Nothing. The text you paste is wrapped into an in-memory object and run through the browser's own decoding function, with no network request at any point, so a URL containing a session token or an internal hostname is never transmitted anywhere. Closing the tab discards it.

Related tools