urldecode
Decodes a URL-encoded string, converting plus signs back into spaces.
urldecode(string $string): string- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string to be decoded. |
Return value
stringThe decoded string, with %XX sequences and + characters resolved.
Errors
Does not throw. Invalid escape sequences are left as literal text.
urldecode() reverses urlencode(): it resolves every %XX escape and, unlike rawurldecode(), it also turns + into a space. That makes it the decoder for query strings and form bodies, and the wrong decoder for path segments.
The dangerous mistake
$_GET, $_POST and $_REQUEST arrive already decoded. Calling urldecode() on them decodes a second time, so a user who submits %252F gets %2F after PHP's pass and a real / after yours. That is how directory traversal and filter bypasses get in. The manual warns about this explicitly; treat any urldecode($_GET[...]) in a codebase as a bug.
Where it is genuinely useful
Decoding a raw query string you read yourself — from a stored URL, a log line, a Referer header, or the string parse_url() hands back. Even there, parse_str() is usually better, because it splits the pairs and decodes them in one step.
Decoding is byte-oriented and never fails: '50% off' comes back unchanged rather than raising an error, so it cannot be used to check that a string was validly encoded.
Example
When to use it
- Decode a single value pulled out of a query string you parsed by hand
- Read parameters from a URL stored in a log file or database column
- Recover the original search term from a Referer header
- Decode form-encoded data received in a raw request body
Frequently asked questions
Do I need to call urldecode on $_GET values?
No, and you should not. PHP decodes $_GET, $_POST and $_REQUEST for you, and decoding twice can turn user-supplied escapes into real slashes or dots.
What is the difference between urldecode and rawurldecode?
urldecode converts + into a space as well as resolving %XX escapes; rawurldecode leaves + alone. Use urldecode for query strings and form data, rawurldecode for path segments.
How do I decode a whole query string into an array?
Use parse_str($query, $params), which splits the pairs and decodes keys and values in one step, including bracketed array notation.
More examples
Parsing a query string by hand, and the better way
Both approaches give are = "green and red" and note = "a&b". Splitting on '=' with a limit of 2 is essential, since a decoded value may itself contain '='. parse_str does the same work in one call and handles bracketed array keys too.
<?php
$query = 'my=apples&are=green+and+red¬e=a%26b';
foreach (explode('&', $query) as $chunk) {
[$k, $v] = array_pad(explode('=', $chunk, 2), 2, '');
printf("%s = %s\n", urldecode($k), urldecode($v));
}
parse_str($query, $params);
print_r($params);Double decoding is a security bug
After PHP's own decoding the value is still the harmless literal %2e%2e%2fetc. One extra urldecode turns it into ../etc, a real traversal sequence — which is exactly why you must not decode superglobals again.
<?php
// What the browser sent: file=%252e%252e%252fetc
$fromPhp = '%2e%2e%2fetc'; // PHP already decoded once
echo "safe : $fromPhp\n";
echo "decoded: " . urldecode($fromPhp) . "\n";
echo "basename of decoded: " . basename(urldecode($fromPhp)) . "\n";Choosing the matching decoder
The first is correct: urldecode restores "C++ notes". The second shows the mismatch — rawurldecode decodes the two %2B escapes but leaves the separator + in place, giving "C+++notes" with no space at all. The third pairing is also correct because %20 needs no plus handling.
<?php
$name = 'C++ notes';
$formStyle = urlencode($name); // C%2B%2B+notes
$pathStyle = rawurlencode($name); // C%2B%2B%20notes
var_dump(urldecode($formStyle));
var_dump(rawurldecode($formStyle));
var_dump(rawurldecode($pathStyle));
Discussion