URL Decode
Paste a percent-encoded string and read it as plain text again: %20 becomes a space, %C3%A9 becomes é. Malformed escapes are reported with their position instead of a bare error, and an option treats + as a space for form-style query strings. You can also switch to encoding. Nothing you paste leaves your browser.
Why This Tool Matters
Encoded URLs are everywhere you would rather not read them. A marketing analyst opens a campaign link and finds the UTM parameters buried in %3D and %26. A developer reads a server log full of request paths like /search%3Fq%3Dcaf%C3%A9, and a support engineer gets a redirect_uri copied from an OAuth error and has to work out what it actually points to. Decoding turns each one back into something you can read, and it takes a second when you do not have to open a console.
Two details trip people up. A + means a space only in form-style query strings (application/x-www-form-urlencoded), while in a path it is a literal plus, so the checkbox lets you choose. And a stray % that is not followed by two hex digits makes decodeURIComponent throw an unhelpful "URI malformed" error. Here the first bad escape is pointed out by position so you can fix it. If a result still looks encoded, such as %2520, the text was encoded twice. Run it through again.
Decoding URLs in JavaScript and Python
The tool uses the same function a browser provides, so the behaviour matches what your code will do. In JavaScript, decodeURIComponent() decodes every escape and throws a URIError on a malformed one. In Python, urllib.parse.unquote() never raises: it leaves a bad % alone and replaces invalid UTF-8 with �, so check your result rather than expecting an exception.
// Decode one value
decodeURIComponent('caf%C3%A9%20au%20lait'); // "café au lait"
// Form-style query strings use + for a space: swap it first
decodeURIComponent('caf%C3%A9+au+lait'.replace(/\+/g, ' '));
// Or let the platform parse a whole query string
new URLSearchParams('q=caf%C3%A9+au+lait').get('q'); // "café au lait"from urllib.parse import unquote, unquote_plus
unquote('caf%C3%A9%20au%20lait') # 'café au lait'
unquote_plus('caf%C3%A9+au+lait') # '+' also becomes a space
unquote('50%') # '50%' (no error for a stray %)How to Use
- 01
Paste the encoded text
Enter a URL, a query string or any text containing %20-style escapes.
- 02
Choose how + is handled
Leave it as is for paths, or tick "Treat + as a space" for form-style query strings.
- 03
Copy the result
Copy the decoded text, or switch to Encode to carry it straight back the other way.
More Text Tools
- Going the other way? Try URL Encode
- Is the value Base64 rather than percent-encoded? Try the Base64 Decoder
- Pulling parameters out of a long link? Try the Regex Tester
- Got a JSON payload out of the decoding? Check it with the JSON Validator
- Browse all Developer Tools
- Need to change letter case first? Try the full Case Converter
Frequently Asked Questions
What does percent-encoding mean, and why does %20 appear in URLs?
URLs can only contain a limited set of characters, so anything else, such as a space, an accented letter or an ampersand inside a value, is replaced by a % followed by the character's byte in hexadecimal. A space is byte 0x20, so it becomes %20. Characters outside ASCII are first turned into UTF-8 bytes, so é becomes the two escapes %C3%A9.
Should + be decoded as a space?
It depends on where the text came from. In an HTML form submission or a typical query string, + stands for a space, and tools such as Python's unquote_plus and JavaScript's URLSearchParams treat it that way. In a URL path, or anywhere else, + is just a plus sign, and decodeURIComponent leaves it alone. A literal plus in form data is written %2B, which this tool keeps correct either way.
Why do I get a "malformed" error?
Each % has to be followed by exactly two hex digits (0–9, A–F). Usually the text contains a literal percent sign that was never escaped, such as "100% fresh", or it was cut off in the middle of an escape. The message names the character position so you can find it. A different error appears when every escape is valid but the bytes are not valid UTF-8, for example %E9 from an old Latin-1 URL.
What is the difference between decodeURI and decodeURIComponent?
decodeURI leaves escapes for characters that structure a URL, such as %2F, %3F and %26, undecoded, while decodeURIComponent decodes them all. This tool uses decodeURIComponent, which is what you want for individual values, but if you decode a complete URL with it a value containing %26 will turn into a real &, changing how it would be parsed.
Why is my decoded text still full of % signs?
It was probably encoded twice. Encoding a string that already contains %20 turns the % into %25, giving %2520. Run the output through the decoder a second time, or toggle to Decode again to peel off the second layer.
Is my text sent to a server when I use this tool?
No. Decoding uses your browser's own decodeURIComponent function, so URLs, tokens and query strings you paste are never uploaded, logged or stored. It keeps working offline once the page has loaded.