Use this URL decode online tool: paste a percent-encoded string and get the original text back immediately. Everything runs in your browser — nothing is uploaded, nothing is logged.
Use %20 only for a URL path, or + also means space for a query/form value. Why the difference?
If you call this “decrypt URL”, the operation here is decoding, not decryption. Paste caf%C3%A9%20%26%20tea to read café & tea. This tool does not unlock encrypted data or bypass a protected link.
A decoded %2520 becomes %20 after one pass. Check whether another layer is intended before converting it to a space. Keep the original link when inspecting reserved characters.
This tool decodes URL percent escapes. It does not decompress an HTTP response, decode every HTTP header format, or turn encrypted traffic into plaintext. The sequence %2B becomes a literal plus; treat + as a space only for form-style input.
To create a parameter value instead of reading it, use URL encoder online. For JavaScript's encodeURIComponent API and its differences from encodeURI, see the JavaScript encoding guide. Do not encode a whole link as though it were one value.
Three steps, none of them magic.

| Encoded | Byte(s) | Decodes to | Note |
|---|---|---|---|
| %20 | 20 | space | One byte, one group. |
| %2B | 2B | + | A real plus sign that was encoded to survive. |
| %C3%A9 | C3 A9 | é | Two bytes, one character. |
| %E2%82%AC | E2 82 AC | € | Three bytes, one character. |
| %26 | 26 | & | Encoded so it is not read as a parameter separator. |
Why strings get encoded in the first place, measured locally: sending GET /hello world HTTP/1.1 over a raw socket to a local server returns 400 Bad request syntax, because the space is the delimiter between the three fields of the request line. %20 travels fine. Encoding is what lets content survive a trip the raw character cannot make.
Two conventions exist, they are not interchangeable, and picking the wrong one silently returns the wrong text.
Below: the same input, a+b, handed to real decoders. Measured on Node.js 22 and Python 3.13.
| Decoder | Input | Result | Reads + as space? |
|---|---|---|---|
| decodeURIComponent('a+b') | a+b | a+b | NO |
| new URLSearchParams('q=a+b').get('q') | a+b | a b | YES |
| urllib.parse.unquote('a+b') | a+b | a+b | NO |
| urllib.parse.unquote_plus('a+b') | a+b | a b | YES |
| urllib.parse.parse_qs('q=a+b') | a+b | {'q': ['a b']} | YES |
The asymmetry worth internalising: in JavaScript, encodeURIComponent('a b') produces a%20b — never a + — yet URLSearchParams.toString() in the same language produces q=a+b. And decodeURIComponent will not turn that + back into a space. Same language, two conventions, and they do not round-trip unless you pair them deliberately.
A server confirms the other half. Three raw requests sent to a local Python server over a socket:
GET /hello%20world HTTP/1.1 -> 200 OK path = /hello%20world
GET /hello+world HTTP/1.1 -> 200 OK path = /hello+world # a literal plus, not a space
GET /hello world HTTP/1.1 -> 400 Bad request syntaxA + in a URL path is a plus sign, full stop. Nothing converts it, and nothing errors — which is what makes it an easy bug to ship.
Both snippets below were run locally; the outputs shown are the actual results.
decodeURIComponent('caf%C3%A9%20%26%20tea') // 'café & tea'
decodeURIComponent('q=caf%C3%A9+tea') // 'q=café+tea' + is kept
// for a query string, use the parser instead:
new URLSearchParams('q=caf%C3%A9+tea').get('q') // 'café tea'
// guard against a stray percent sign:
function safeDecode(s){ try { return decodeURIComponent(s); } catch { return s; } }from urllib.parse import unquote, unquote_plus, parse_qs
unquote('caf%C3%A9%20%26%20tea') # 'café & tea'
unquote('q=caf%C3%A9+tea') # 'q=café+tea' + is kept
unquote_plus('q=caf%C3%A9+tea') # 'q=café tea'
parse_qs('q=caf%C3%A9+tea') # {'q': ['café tea']}Rule of thumb: unquote / decodeURIComponent for a path segment; unquote_plus / URLSearchParams for a query string or form value.
People use “encoded URL” to mean a URL that contains percent-encoded characters. A percent escape has the form %HH, where HH is a pair of hexadecimal digits representing one byte. For example, %20 is the byte value 0x20, commonly used for a space in a URL component.
That does not mean every character in the entire link has been replaced. A URL has parts—such as its scheme, host, path, query, and fragment—and visible characters like ?, =, and & often separate those parts. Percent encoding applies to data in a component; those structural separators usually need to remain recognizable.
For non-ASCII text, a character may become several UTF-8 bytes, and each byte gets its own percent escape. The check mark ✓, for example, is UTF-8 bytes E2 9C 93, so its percent-encoded form is %E2%9C%93. Three escapes here represent one Unicode character.
The percent escapes alone do not tell you every detail of the original input. The decoder also needs the correct character encoding and the right component boundary. Percent encoding is a representation, not encryption: anyone with the URL can decode it.
Consider a redirect URL whose next parameter contains another URL. The outer query has to encode characters that belong to the inner link. When the outer query is parsed, its parameter value becomes the inner URL; then parse that inner URL to read its own query:
https://example.com/redirect?next=https%3A%2F%2Fshop.example%2Fsearch%3Fq%3Dtea%2520cake
Parsing next yields:
https://shop.example/search?q=tea%20cake
Parsing the inner query value q then yields tea cake.
In JavaScript, use the URL parser for each layer instead of manually decoding the full string repeatedly:
const outer = new URL("https://example.com/redirect?next=https%3A%2F%2Fshop.example%2Fsearch%3Fq%3Dtea%2520cake");
const innerText = outer.searchParams.get("next");
const inner = new URL(innerText);
const query = inner.searchParams.get("q"); // "tea cake"Here the outer query parser removes one encoding layer to recover the inner URL. The inner query parser then reads its q value. Each parser handles the structure for its own URL.
Percent-encoding makes data transportable inside a URI component; it does not guarantee that every proxy, router, and application will interpret that component the same way. A reserved character such as %2F may affect path routing, while a query value may be decoded by the framework before your handler sees it. Encode the individual value once, preserve the URL's structural separators, and decode it once at the matching component boundary. When diagnosing a failure, compare the outgoing request target, the value after proxy processing, and the value delivered to the application. Avoid decoding the whole URL before parsing it. See RFC 3986 §2.2 for reserved delimiters.
Paste the percent-encoded URL above and choose Decode · %20 only; the result appears immediately in your browser.
It means converting percent escapes such as %20 and %C3%A9 back to characters without uploading the input.
URL decode is the operation; a URL decoder is the tool that performs it. This page provides both the input box and the result.
Use form mode when + represents a space in a query string; use percent-only mode when a literal plus must remain +.
It is another ordering of the same request: paste the encoded URL here and decode one layer at a time.
Yes. Paste it above; if the output still contains %XX, the value may be double-encoded and needs a second deliberate pass.
When a URL contains query parameters, keep the original sequence before converting it to an object. getAll() preserves repeated keys; empty= has an empty value; and flag has no equals sign at all.
| Input | State | What to preserve |
|---|---|---|
tag=a&tag=b | Repeated key | Both values; get() returns only the first, while getAll() returns both. |
empty= | Empty value | The key exists and its value is the empty string. |
flag | Valueless key | The key appears without an equals sign in the raw query. |
Converting the entries to a plain object can discard repeated keys because one property name can hold only one final value. Inspect the sequence first when the distinction matters.
How do I decode a URL online?
Paste the encoded string into the box at the top of this page and pick the mode that matches where it came from: %20 only for a URL path, + also means space for a query string or form value. It runs in your browser — the string is never sent anywhere.
Why does %20 become a space but + stays a plus?
Because the two conventions disagree. Standard percent-decoding only understands %XX. The + → space rule comes from HTML form encoding, and only query-string parsers that copy it apply it — measured above: decodeURIComponent('a+b') returns a+b, while URLSearchParams('q=a+b').get('q') returns a b.
What is %2520?
A space that was encoded twice. The first pass made %20; the second encoded the % into %25. Decoding once gives back the text %20; decoding twice gives the space.
Is decoding the same as decrypting?
No. Percent-encoding is a reversible public format — decoding needs no key and anyone can do it. It exists so that characters survive transport, not to hide anything. Never treat an encoded string as protected data.
Does letter case matter in %3a vs %3A?
No. The hex digits are case-insensitive and both decode to :. Uppercase is the conventional output form, but either decodes correctly.
See how percent escapes recover bytes first, then a character encoding such as Shift JIS interprets those bytes. This is a separate step from the page tool’s UTF-8 decoder.
Decoding runs entirely in your browser — no requests, no uploads. Measured results on this page were produced on 2026-09-30 against Node.js 22 and Python 3.13, with the request-line results coming from raw TCP requests to a local server.
Follow percent bytes back into UTF-8 text, including the difference between a literal plus and a form-encoded space.
Every tool on this site runs entirely in your browser. Pick the encoding problem you have: