URL Decode

Decode percent-encoded URLs

What is URL Decoding?

URL decoding (percent-decoding) turns a percent-encoded string back into readable text. Browsers, servers, and APIs encode any character that isn’t safe inside a URL as a % followed by two hexadecimal digits, as defined by RFC 3986. A URL decoder reverses that: %20 becomes a space, %26 becomes &, and %E2%9C%93 becomes ✓.

Decoding happens in two steps, and the second one is where most bugs live:

  1. Bytes. Every %XX triplet is converted into one byte; ordinary characters pass through unchanged.
  2. Characters. The resulting byte sequence is interpreted as UTF-8 to rebuild the original text.

For example, caf%C3%A9%20%E2%98%95 decodes like this:

InputBytesResult
caf63 61 66caf
%C3%A9C3 A9é (two-byte UTF-8)
%2020space
%E2%98%95E2 98 95☕ (three-byte UTF-8)

Final output: café ☕. A single visible character can come from one, two, three, or four escape triplets, so decoding one triplet at a time gives you gibberish. The bytes have to be collected and read as UTF-8 together.

How to Decode a URL with This Tool

  1. Paste the encoded string, such as a query parameter value, a log line, or a whole link.
  2. Press Ctrl+Enter or click Decode. The tool applies decodeURIComponent to the input, so every escape is decoded, including %2F, %3F, and %23.
  3. Parsing a full URL? Click Parse URL instead. The URL is split into protocol, host, path, and fragment, and every query parameter is listed on its own line as key = value, decoded with form rules (so + becomes a space). This needs a complete URL that starts with a scheme such as https://.
  4. Copy the result with one click or Ctrl+Shift+C, and clear the input with Ctrl+L.

Leading and trailing whitespace is trimmed before decoding, so stray newlines copied from a terminal or email won’t cause errors.

Decode the Parameter, Not the Whole URL

The most common decoding mistake is running a complete URL through a decoder and trusting the result. Encoding exists to protect characters that would otherwise change the URL’s structure. Take this redirect link:

https://example.com/login?next=%2Fcart%3Fitem%3D42%26qty%3D2&lang=en

Decoding the whole thing gives ...?next=/cart?item=42&qty=2&lang=en. Now qty=2 looks like a separate parameter of the outer URL, but it actually belongs inside next. Decode the value of next on its own and you get the correct /cart?item=42&qty=2. The Parse URL button does this for you by splitting parameters before decoding each one.

The same rule applies to nested links. Click-tracking and redirect services wrap the real destination in a parameter: Google’s /url?q=, Microsoft Outlook Safe Links (safelinks.protection.outlook.com/?url=), and most email-marketing trackers. The destination is sometimes encoded more than once, so decode one layer at a time until you reach a URL with no %XX sequences left. It’s a quick way to see where a suspicious link really goes without clicking it.

Percent-Encoded Sequences You’ll Meet in the Wild

These are the escapes that most often confuse people reading logs, analytics exports, and pasted links:

EncodedDecodedWhere you usually see it
%0A / %0D%0Aline feed / CRLFMulti-line form fields, textarea submissions
%09tabPasted spreadsheet data
%2C,Comma-separated filter lists (tags=a%2Cb)
%3A:Timestamps (12%3A30), nested URLs
%5B%5D[]Array parameters (ids%5B%5D=1&ids%5B%5D=2)
%7C|Pipe-delimited analytics values
%27'Names and search queries with apostrophes
%C2%A0non-breaking spaceText copied from web pages or Word
%E2%80%99’“Smart” apostrophe from rich-text editors
%E2%80%9C / %E2%80%9D“ / ”Smart quotes
%E2%80%93–En dash in titles and slugs
%EF%BB%BFbyte order mark (invisible)Values read from a UTF-8 file with a BOM

The last few matter because they decode to characters that look normal but aren’t. For example, %C2%A0 produces a space that won’t match a regular space in comparisons, and %EF%BB%BF produces an invisible character that breaks equality checks.

Common URL Decoding Errors

“URI malformed”

The decoder hit a % it couldn’t turn into valid UTF-8. Three causes cover almost every case:

  • A literal percent sign that was never encoded. discount=100% should have been discount=100%25. Replace the bare % with %25 and decode again.
  • A truncated string. %E2%82 is the first two-thirds of the euro sign %E2%82%AC. This happens when a URL gets cut off by a character limit, a log column width, or a chat client.
  • Latin-1 instead of UTF-8. Older systems encode é as %E9 (one byte) instead of %C3%A9 (two bytes). %E9 on its own isn’t valid UTF-8, so a standards-compliant decoder rejects it.

+ Doesn’t Turn Into a Space

decodeURIComponent treats + as a literal plus sign, which is correct for paths and for anything built with encodeURIComponent. HTML forms and many query strings use the older application/x-www-form-urlencoded format, where + means a space. If hello+world should read hello world, use Parse URL on the full link, or a form-aware decoder in code (see the table below).

Output Still Contains %XX Sequences

If decoding report%252Fq3 gives you report%2Fq3, the string was double-encoded: the % of the original %2F was itself encoded as %25. Decode a second time to get report/q3. Repeat until no unintended %XX escapes remain, then fix the code that encoded the value twice.

%uXXXX Sequences

Strings like %u20AC come from JavaScript’s deprecated escape() function, not from standard URL encoding. They aren’t valid percent-encoding, so decodeURIComponent throws on them. In a browser console, unescape('%u20AC') returns € for a one-off conversion. The same trick decodes Latin-1 escapes like %E9. The long-term fix is to replace escape() with encodeURIComponent() at the source.

URL Decode in Code

Most languages ship two decoders: one that follows strict percent-decoding rules and one that follows HTML-form rules. Picking the wrong one is the root cause of most “+ vs space” bugs.

LanguagePercent-decoding (+ stays +)Form decoding (+ → space)
JavaScriptdecodeURIComponent(s)new URLSearchParams(query).get(key)
Pythonurllib.parse.unquote(s)urllib.parse.unquote_plus(s), parse_qs(query)
PHPrawurldecode($s)urldecode($s)
Gourl.PathUnescape(s)url.QueryUnescape(s)
Java—URLDecoder.decode(s, StandardCharsets.UTF_8)

A few behaviors worth knowing:

  • Python’s unquote doesn’t raise. Invalid UTF-8 becomes the replacement character �, and a stray % is left as it is. Pass encoding='latin-1' to decode legacy %E9-style strings.
  • PHP’s $_GET and $_POST are already decoded. Calling urldecode() on them again is a double-decode bug.
  • Go and Java return errors on malformed escapes, much like JavaScript’s URIError.

For a quick decode in a terminal, Python is usually installed already:

python3 -c 'import sys, urllib.parse as u; print(u.unquote(sys.argv[1]))' 'caf%C3%A9'

When You Need a URL Decoder

  • Reading logs and analytics. Server access logs, CDN logs, and UTM parameters store paths and queries in encoded form. Decoding shows the actual search terms and campaign names.
  • Debugging OAuth. A redirect_uri_mismatch error usually comes down to one encoded character in the redirect_uri parameter. Decode it and compare it character by character with the URI registered with the provider.
  • Inspecting webhooks. Slack slash commands and Twilio webhooks send application/x-www-form-urlencoded bodies. Paste the raw body to see the fields.
  • Handling S3 event notifications. Amazon S3 URL-encodes object keys in event payloads, so red flower.jpg arrives as red+flower.jpg. Use a form decoder such as unquote_plus before calling GetObject, or the lookup fails.
  • Checking links before you click. Decoding a long tracking link shows its real destination and any email address or identifier embedded in it.

Decode Exactly Once: a Security Note

In application code, decode each input once, at the boundary, and then validate the decoded value. Decoding again after validation is a classic vulnerability. A filter that blocks ../ can be bypassed with %252e%252e%252f: after the first decode it reads %2e%2e%2f, which passes the check, and only a second, later decode turns it into ../. Web application firewalls and security reviews treat repeated decoding as a red flag for exactly this reason. When you inspect a suspicious URL, decoding layer by layer is fine. The rule applies to code paths that act on the result.

This decoder runs entirely in your browser, so you can paste URLs that contain session IDs, OAuth codes, or signed links without sending them anywhere. To go the other way, use the URL Encode tool or the combined URL Encoder & Decoder. If a decoded value turns out to be Base64 or a JWT, continue with the Base64 decoder or the JWT Decoder.

Frequently Asked Questions

How do I decode a URL?

Paste the percent-encoded string into the input box and press Ctrl+Enter (or click Decode). Every `%XX` sequence is converted back into its original character — `%20` becomes a space, `%2F` becomes `/`, `%C3%A9` becomes `é`. If you have a complete URL with a query string, click Parse URL instead to see each query parameter decoded on its own line.

What does %20 mean in a URL?

`%20` is a percent-encoded space. URLs can't contain a literal space, so it is replaced by `%` plus the character's hexadecimal byte value — space is byte `0x20`. You'll also see `+` used for spaces, but only inside query strings and HTML form submissions.

Why do I get a "URI malformed" error when decoding?

The input contains a `%` that isn't followed by a valid UTF-8 byte sequence. The usual causes are a literal percent sign that was never encoded (`100%` instead of `100%25`), a string truncated in the middle of a multi-byte character (`%E2%82`), or text encoded in Latin-1 rather than UTF-8 (`%E9` for `é` instead of `%C3%A9`). Fix the stray `%` or re-encode the source as UTF-8, then decode again.

Why doesn't + turn into a space when I decode?

The Decode button follows `decodeURIComponent` rules, where `+` is a literal plus sign. Only the `application/x-www-form-urlencoded` format (HTML forms and query strings) treats `+` as a space. Paste the full URL and use Parse URL, which decodes query parameters with form rules, or use a form-aware function in code such as Python's `unquote_plus` or PHP's `urldecode`.

What is the difference between decodeURI and decodeURIComponent?

`decodeURIComponent` decodes every escape sequence, including `%2F`, `%3F`, `%26`, and `%23`. `decodeURI` leaves the escapes for reserved characters (`; / ? : @ & = + $ , #`) untouched so that the structure of a full URL doesn't change. Use `decodeURIComponent` on a single parameter value; use `decodeURI` only when you must decode a whole URL without breaking its separators.

How do I URL decode a string in Python?

Use `urllib.parse.unquote('caf%C3%A9')` for percent-decoding, or `urllib.parse.unquote_plus('a+b')` when `+` should become a space (form data, S3 event keys). To split and decode a whole query string in one step, use `urllib.parse.parse_qs('q=hello+world&tag=a%26b')`, which returns `{'q': ['hello world'], 'tag': ['a&b']}`.

Is it safe to decode URLs that contain tokens or passwords?

Yes, with this tool. Decoding runs entirely in your browser with JavaScript — nothing is uploaded or logged. That matters because decoded URLs often expose OAuth codes, access tokens, signed download signatures, and email addresses that you wouldn't want sitting in a third-party server log.