URL Decoder

Paste percent-encoded text and read it. This is usually a query parameter or a redirect target. If the output still shows sequences like %3A%2F%2F, the input was encoded twice (%253A%252F%252F), so paste the output back in and decode once more.

Input Text
Decoded Output

1 URL Decoder example

Encoded Input

1
https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world%26lang%3Den

Decoded Output

1
https://example.com/search?q=hello world&lang=en

2 Two common uses

Reading a redirect

OAuth and SSO flows nest a whole URL inside a query parameter, such as redirect_uri, encoded once or twice. Decode it to check where the flow sends the user.

Debugging form data

Spaces arrive as %20 or +, and which one you see tells you how the request was built. The decoder follows decodeURIComponent, so + stays a plus sign.

How to use the URL Decoder tool

  1. Paste the encoded string into the left editor, or click Upload File to read it from a text file. Click Dummy Data to load a sample.
  2. The right panel decodes instantly with decodeURIComponent(). Percent sequences such as %20 and multi-byte UTF-8 sequences such as %C3%A9 turn back into characters.
  3. If the input contains a stray % that is not followed by two hex digits, the tool shows "Error: URI malformed" above the editors. Fix or remove that character and the output returns.
  4. For a double-encoded value, copy the output back into the input and decode a second time.
  5. Click Copy Result to copy the decoded text, or Clear to reset both panels.

URL decoding in JavaScript, PHP and Python, edge cases included

JavaScript / Node.js

const encoded = "hello%20world%20%26%20more%2F100%25";

console.log(decodeURIComponent(encoded));
// hello world & more/100%

// Form data uses + for spaces: swap them before decoding
const fromForm = "first+name=John+Doe";
console.log(decodeURIComponent(fromForm.replace(/\+/g, " ")));

// A stray % throws URIError: URI malformed
try {
  decodeURIComponent("100%");
} catch (err) {
  console.error(err.name, err.message);
}

Always wrap decodeURIComponent() in try/catch when the input comes from users or logs.

PHP

<?php
$encoded = "hello%20world%20%26%20more%2F100%25";

// Decodes %XX only; a + stays a +
echo rawurldecode($encoded), PHP_EOL;

// Decodes %XX and turns + into a space (form data)
echo urldecode("first+name=John+Doe"), PHP_EOL;

// Decode a full query string into an array in one step
parse_str("q=hello%20world&tags%5B%5D=a&tags%5B%5D=b", $params);
print_r($params);

PHP never throws on bad sequences. An invalid % is passed through unchanged.

Python

from urllib.parse import unquote, unquote_plus

encoded = "hello%20world%20%26%20more%2F100%25"

# %XX only; + is left alone
print(unquote(encoded))

# %XX and + to space, for form-encoded data
print(unquote_plus("first+name=John+Doe"))

# Python never raises on bad sequences: a lone % is returned as-is
print(unquote("100%"))

Pass errors="strict" to unquote() if you want a UnicodeDecodeError on invalid UTF-8 instead of replacement characters.

Common errors and how to fix them

URIError: URI malformed
A % is not followed by two hex digits, or the bytes do not form valid UTF-8. Usually the value was never encoded and contains a literal percent sign. Encode it as %25 at the source, or catch the error and return the raw input.
Decoded text still contains %20 or %2F
The value was encoded twice. Decode it again, then find where the second encoding happens. Common causes are a template that calls encodeURIComponent on an already-encoded value, and HTTP libraries that encode query values automatically.
Plus signs remain in the decoded output
decodeURIComponent() and this tool treat + literally, following RFC 3986. Form submissions use + for spaces, so replace + with a space first, or use URLSearchParams, urldecode() or unquote_plus(), which do it for you.
Accented characters come out as é or question marks
The bytes were encoded as Latin-1 instead of UTF-8, which is common with old PHP or Java code. Decode the bytes with the same charset that produced them, then re-encode as UTF-8.

Good to know

Split a URL on its delimiters before you decode it. decodeURIComponent() turns %2F into a slash even though encodeURI() would never have produced it, so decoding a whole URL in one pass can create path separators out of what was data. Split first, then decode each piece.

Decode once, as late as possible. Express, Laravel and Django all decode query values and route parameters before your handler runs, so decoding those again is where most double-decoding bugs come from. If a string already reads as plain text with a stray % in it, it has probably been decoded already, so leave it alone.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Guides and tutorials

Frequently Asked Questions

What does the decoder do?

It converts percent-encoded sequences such as %20 and %C3%A9 back into the original characters (a space and é), so an encoded query value becomes readable text.

Why does my URL look double-encoded?

A value was encoded twice, which turns %20 into %2520. Decode once to get %20 and once more to get the space. Then fix the code that encodes twice, usually a manual encode followed by an HTTP client that encodes again.

Are plus signs converted to spaces?

No. Plus signs are left as they are, matching decodeURIComponent(). If the string came from a form submission, replace + with a space before you decode.

What causes a "malformed URI" error?

A percent sign not followed by two hex digits, such as a literal % in 50% off, or bytes that do not form valid UTF-8. Encode the stray percent as %25 at the source.

Is the input sent to a server?

No. The tool calls decodeURIComponent() in your browser tab, and uploaded files are read locally, so nothing is sent to a server.

Related

The URL encoder goes the other way. To see the parameters as a list instead of one long string, use the query string parser or the URL parser.

More URL Tools

Browse all url tools

Copied to clipboard!