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:
- Bytes. Every
%XXtriplet is converted into one byte; ordinary characters pass through unchanged. - 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:
| Input | Bytes | Result |
|---|---|---|
caf | 63 61 66 | caf |
%C3%A9 | C3 A9 | é (two-byte UTF-8) |
%20 | 20 | space |
%E2%98%95 | E2 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
- Paste the encoded string, such as a query parameter value, a log line, or a whole link.
- Press
Ctrl+Enteror click Decode. The tool appliesdecodeURIComponentto the input, so every escape is decoded, including%2F,%3F, and%23. - 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 ashttps://. - Copy the result with one click or
Ctrl+Shift+C, and clear the input withCtrl+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:
| Encoded | Decoded | Where you usually see it |
|---|---|---|
%0A / %0D%0A | line feed / CRLF | Multi-line form fields, textarea submissions |
%09 | tab | Pasted 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%A0 | non-breaking space | Text 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%BF | byte 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 beendiscount=100%25. Replace the bare%with%25and decode again. - A truncated string.
%E2%82is 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).%E9on 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.
| Language | Percent-decoding (+ stays +) | Form decoding (+ → space) |
|---|---|---|
| JavaScript | decodeURIComponent(s) | new URLSearchParams(query).get(key) |
| Python | urllib.parse.unquote(s) | urllib.parse.unquote_plus(s), parse_qs(query) |
| PHP | rawurldecode($s) | urldecode($s) |
| Go | url.PathUnescape(s) | url.QueryUnescape(s) |
| Java | — | URLDecoder.decode(s, StandardCharsets.UTF_8) |
A few behaviors worth knowing:
- Python’s
unquotedoesn’t raise. Invalid UTF-8 becomes the replacement character�, and a stray%is left as it is. Passencoding='latin-1'to decode legacy%E9-style strings. - PHP’s
$_GETand$_POSTare already decoded. Callingurldecode()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_mismatcherror usually comes down to one encoded character in theredirect_uriparameter. 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-urlencodedbodies. Paste the raw body to see the fields. - Handling S3 event notifications. Amazon S3 URL-encodes object keys in event payloads, so
red flower.jpgarrives asred+flower.jpg. Use a form decoder such asunquote_plusbefore callingGetObject, 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.