URL Decode Online: URL Decoder

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.

← Back to the URL Encoder

Paste a URL or encoded text

Use %20 only for a URL path, or + also means space for a query/form value. Why the difference?

Decrypt URL or decode link: restore percent-encoded text

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.

Decode URL online: inspect one layer at a time

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.

HTTP decode: percent escapes are one format

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.

Encode URI component: the opposite direction

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.

① What decoding actually does

Three steps, none of them magic.

Decoding caf%C3%A9 by reading percent escapes as UTF-8 bytes and rebuilding café.
Read each %XX escape as a byte, then decode the byte sequence as UTF-8.
  1. Read each %XX group as one byte — %20 is byte 0x20, %C3 is byte 0xC3.
  2. Join the bytes back into a UTF-8 sequence — this is why %C3%A9 is a single character, é, and not two.
  3. Leave every other character alone — : / ? & = pass straight through, because they were never encoded.
EncodedByte(s)Decodes toNote
%2020spaceOne byte, one group.
%2B2B+A real plus sign that was encoded to survive.
%C3%A9C3 A9éTwo bytes, one character.
%E2%82%ACE2 82 AC€Three bytes, one character.
%2626&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.

② The one choice that changes your answer: +

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.

DecoderInputResultReads + as space?
decodeURIComponent('a+b')a+ba+bNO
new URLSearchParams('q=a+b').get('q')a+ba bYES
urllib.parse.unquote('a+b')a+ba+bNO
urllib.parse.unquote_plus('a+b')a+ba bYES
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:

Measured · raw request → what the server received
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 syntax

A + 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.

③ Four reasons a decoder returns the wrong thing

  1. The string was encoded twice. %20 encoded again becomes %2520, because the second pass encodes the % into %25. Decoding once returns the text %20, not a space. Measured in both runtimes: decodeURIComponent('a%2520b') → a%20b, and unquote('a%2520b') → a%20b. Seeing %25 inside a URL is the signature of a double encode — decode twice, or better, fix the code that encoded twice.
  2. The wrong + convention. Covered above: c++ in a query value becomes c   under the form convention. That is why an encoder writes a genuine plus as %2B.
  3. A stray or truncated %. Measured: decodeURIComponent('%') and decodeURIComponent('%2') both throw URI malformed. A percent sign left over from string concatenation does not degrade gracefully — it throws. If your decoder is failing on user input, look for a bare %.
  4. Hex case is not the problem — but it looks like one. Measured: decodeURIComponent('%3a') and decodeURIComponent('%3A') both return :. Lowercase and uppercase hex decode identically, so %3a is never the reason a decode failed.

④ Doing it in your own code

Both snippets below were run locally; the outputs shown are the actual results.

JavaScript
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; } }
Python
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.

What does “encoded URL” mean?

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.

Read the percent escapes as bytes

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.

Decode nested URLs one layer at a time

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.

Why does an encoded URI with special characters still fail on the server?

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.

GSC query answers

How do I decode a URL?

Paste the percent-encoded URL above and choose Decode · %20 only; the result appears immediately in your browser.

What does URL decode online mean?

It means converting percent escapes such as %20 and %C3%A9 back to characters without uploading the input.

What is the difference between URL decode and URL decoder?

URL decode is the operation; a URL decoder is the tool that performs it. This page provides both the input box and the result.

How do I decode a URL with plus signs?

Use form mode when + represents a space in a query string; use percent-only mode when a literal plus must remain +.

What does decoder URL mean?

It is another ordering of the same request: paste the encoded URL here and decode one layer at a time.

Can I decode an already encoded URL online?

Yes. Paste it above; if the output still contains %XX, the value may be double-encoded and needs a second deliberate pass.

Query parameter states: repeated, empty, and valueless

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.

InputStateWhat to preserve
tag=a&tag=bRepeated keyBoth values; get() returns only the first, while getAll() returns both.
empty=Empty valueThe key exists and its value is the empty string.
flagValueless keyThe 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.

FAQ

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.

Watch: Shift JIS and URL decoding

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.

Watch on YouTube

Where to go next

→ URL Encoder / Decoder (full tool) → Percent-encoding explained from first principles → URL encoding in JavaScript, with code examples

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.

Watch: How URL Decoding Works

Follow percent bytes back into UTF-8 text, including the difference between a literal plus and a form-encoded space.

Watch on YouTube

Related tools

Every tool on this site runs entirely in your browser. Pick the encoding problem you have: