How to Decode a URL: Percent-Encoding Made Simple

Try the URL Encoder
Article illustration: How to Decode a URL: Percent-Encoding Made Simple

Decoding a URL means reversing percent-encoding so that sequences like %20, %2F, and %C3%A9 turn back into the characters they represent — a space, a slash, and é. You decode URLs constantly, often without noticing: when you read a server log, debug a redirect, inspect an analytics tag, or pull a value out of a query string. This guide explains exactly how decoding works, how to do it in every major language, and how to avoid the bugs that trip people up.

If you just need the answer right now, paste your text into the URL decoder, switch the direction toggle to Decode, and read the output. The rest of this article explains what is happening underneath so you can fix problems when the output is not what you expect.

What URL Decoding Actually Does

A percent-encoded sequence is always a % followed by exactly two hexadecimal digits. Decoding reads those two digits as a byte value and replaces the three-character sequence with the corresponding character:

  • %20 → space
  • %2F/
  • %3D=
  • %26&
  • %3F?
  • %23#

Any character in the URL that is not part of a %XX sequence is left exactly as it is. So decoding hello%20world gives hello world, while hello-world stays hello-world because there is nothing to decode.

The one historical exception is the plus sign. In the application/x-www-form-urlencoded format used by HTML form submissions and many query strings, a literal space is written as + rather than %20. A strict URL decoder leaves + as a plus sign; a form decoder turns + into a space. This single difference is responsible for a surprising number of bugs, and we come back to it below.

Decoding Multi-Byte (UTF-8) Characters

Characters outside the ASCII range are not encoded as a single %XX. They are first converted to their UTF-8 bytes, and then each byte is percent-encoded. That is why one visible character can become several percent sequences:

é   → %C3%A9      (2 bytes)
€   → %E2%82%AC   (3 bytes)
😀  → %F0%9F%98%80 (4 bytes)

A correct decoder reads the whole byte sequence and reassembles the original character. This is important: you cannot decode %C3%A9 one byte at a time and get a sensible result, because %C3 and %A9 are not valid characters on their own — they only mean é when interpreted together as UTF-8. Every example on this page, and the URL decoder tool, handles this for you, but it explains why a "broken" decoder that processes bytes individually produces garbled text like é instead of é.

How to Decode a URL in Code

Every mainstream language ships a decoder in its standard library. Reach for these instead of writing your own.

JavaScript

Use decodeURIComponent() for a single value (a query parameter, a path segment) and decodeURI() for a complete URL where you want to keep the structure intact:

decodeURIComponent("red%20shoes%20%26%20socks");
// "red shoes & socks"

decodeURI("https://example.com/search?q=red%20shoes");
// "https://example.com/search?q=red shoes"

For query strings, URLSearchParams decodes automatically and also handles + as a space:

const params = new URLSearchParams("q=red+shoes&page=2");
params.get("q");    // "red shoes"
params.get("page"); // "2"

Python

from urllib.parse import unquote, unquote_plus

unquote("red%20shoes%20%26%20socks")
# 'red shoes & socks'

unquote_plus("red+shoes")  # form style: '+' becomes a space
# 'red shoes'

Use unquote() for standard percent-decoding and unquote_plus() when the value came from a form submission.

PHP

echo rawurldecode("red%20shoes");  // "red shoes"  (strict)
echo urldecode("red+shoes");       // "red shoes"  (treats + as space)

rawurldecode() follows RFC 3986 and leaves + alone; urldecode() is form-style and converts + to a space.

Java

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

URLDecoder.decode("red+shoes%20%26%20socks", StandardCharsets.UTF_8);
// "red shoes & socks"   (note: + becomes a space)

Java's URLDecoder is form-style, so it always treats + as a space. Always pass the UTF-8 charset to avoid platform-dependent results.

The Double-Encoding Trap

The single most common decoding problem is double encoding, and its signature is the sequence %25. Because %25 is the encoding of the percent sign itself, encoding a value twice nests the escapes:

"a b"  encoded once  → "a%20b"
"a%20b" encoded again → "a%2520b"

To recover the original you must decode the same number of times it was encoded. Decoding a%2520b once gives a%20b; decoding it a second time gives a b. If you see %2520, %252F, or %253D in logs or URLs, something in your pipeline encoded an already-encoded value. The fix is almost never to decode twice forever — it is to find the place that encodes a value that was already encoded and remove the extra step. You can confirm what you are dealing with by pasting the string into the decoder and decoding one level at a time.

When Decoding Goes Wrong

A few other failure modes are worth recognizing:

  • A bare % that is not followed by two hex digits. Strict decoders throw an error (JavaScript's decodeURIComponent raises URIError: URI malformed). If a real percent sign needs to survive, it must be encoded as %25.
  • % followed by non-hex characters, such as %ZZ. This is invalid and will either error or be left untouched depending on the decoder.
  • Wrong charset assumptions. If a value was encoded from a non-UTF-8 byte stream, a UTF-8 decoder will produce replacement characters. Almost all modern systems use UTF-8, so this is rare today but still appears with legacy data.
  • Decoding before splitting. Never decode a full query string before you split it on & and =. If a value legitimately contained an encoded %26, decoding first turns it into a real & and your splitter breaks the value into two. Split first, then decode each piece.

A Quick Decoding Checklist

Use this when a decoded value does not look right:

  1. Is it a single value or a whole URL? Use decodeURIComponent / unquote / rawurldecode for values, and the structure-aware variant for whole URLs.
  2. Did the data come from a form or a query string where + means space? If so, use the form-style decoder (URLSearchParams, unquote_plus, urldecode).
  3. Do you see %2520 or %252F? That is double encoding — decode one extra level and fix the source.
  4. Is non-Latin text showing as é or €? The bytes were decoded individually or with the wrong charset; decode the full UTF-8 sequence as one unit.
  5. Still stuck? Paste the raw string into the URL decoder and decode step by step to see exactly where it changes.

Frequently Asked Questions

How do I decode %20 in a URL?

%20 is the percent-encoding for a space. Any standard URL decoder converts it back: decodeURIComponent("a%20b") in JavaScript, unquote("a%20b") in Python, or rawurldecode("a%20b") in PHP all return a b. In the URL decoder, switch the toggle to Decode and %20 becomes a space in the output.

What is the difference between %20 and + in a URL?

Both can represent a space, but in different contexts. %20 is the universal percent-encoding for a space and is valid anywhere in a URL. + means a space only inside the application/x-www-form-urlencoded format, which is used for form submissions and many query strings. A strict decoder leaves + as a plus sign, while a form-style decoder turns it into a space. See the query parameters guide for details on where each applies.

Why does my decoded text show strange characters like é?

That happens when multi-byte UTF-8 sequences are decoded one byte at a time, or with the wrong character set. The characters é are the result of reading the two bytes of é (%C3%A9) as separate Latin-1 characters instead of one UTF-8 character. Use a UTF-8-aware decoder and decode the full sequence together. The UTF-8 and Unicode guide explains this in depth.

Do I need to decode a URL before using it in a browser?

Usually not. Browsers and HTTP libraries expect URLs to be in their encoded form on the wire, so you should keep the encoded version when making requests. You decode only when you want to display or read a value, or when you extract a parameter to use as data in your application. Decoding a URL and then putting it back into an href can actually break it.

How do I decode a URL that was encoded twice?

Decode it the same number of times it was encoded. If you see %2520, the value was encoded twice, so decoding once gives %20 and decoding a second time gives a space. The real fix is to find the code that encodes an already-encoded value and remove the duplicate encoding step rather than decoding twice everywhere downstream.

Is it safe to decode URLs with this tool?

Yes. The URL decoder runs entirely in your browser using JavaScript, so the text you paste is decoded locally and is not uploaded for processing. That makes it safe for inspecting tokens, redirect URLs, and other sensitive values while you debug.

Encode URLs Instantly

Encode and decode URLs with full Unicode support, multiple encoding modes, and batch processing.

Open URL Encoder