$_REQUEST
$_REQUEST
What Is $_REQUEST?
In simple terms: $_REQUEST is an array that combines data from several sources — usually the URL query string ($_GET) and submitted form data ($_POST) — into one place. Technically: $_REQUEST is a superglobal built by PHP at the start of each request from $_GET, $_POST, and possibly $_COOKIE. Which sources are included, and in which order they override each other, is controlled by the request_order setting (or variables_order if request_order is empty).
Why It Exists
$_REQUEST was designed for convenience: a script could read a value without caring whether it came from a link (GET) or a form (POST). In practice, this convenience creates ambiguity and security problems, which is why modern PHP code avoids $_REQUEST. You still need to understand it to read and maintain older code.
How $_REQUEST Is Built
The request_order Setting
The recommended php.ini files that ship with PHP (php.ini-development and php.ini-production) set:
request_order = "GP"
This means:
- Start with
$_GETvalues. - Add
$_POSTvalues; when a key exists in both, the POST value overwrites the GET value. - Cookies are not included. If
request_orderis empty, PHP falls back tovariables_order. Its built-in default ("EGPCS") includes cookies, so on servers without the recommended configuration, cookie values can end up in$_REQUESTand override GET and POST data.
Example: The Same Key in Two Sources
Consider a form submitted to a URL that also has a query parameter with the same name:
POST /update.php?id=10 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
id=25&status=shipped
<?php
echo "GET id: " . ($_GET['id'] ?? 'none') . "\n";
echo "POST id: " . ($_POST['id'] ?? 'none') . "\n";
echo "REQUEST id: " . ($_REQUEST['id'] ?? 'none') . "\n";
echo "REQUEST status: " . ($_REQUEST['status'] ?? 'none') . "\n";
Output (with request_order = "GP")
GET id: 10
POST id: 25
REQUEST id: 25
REQUEST status: shipped
$_REQUEST['id'] contains the POST value, because POST comes after GET in the order. Code reading $_REQUEST['id'] gets 25 without any indication that a different id was in the URL. If the server's configuration differs, the same code might get 10 — or a cookie value.
Why $_REQUEST Should Be Avoided
1. Ambiguity
The code no longer states where data comes from. A reader cannot tell whether $_REQUEST['email'] is expected from a form, a link, or a cookie, and the answer depends on server configuration.
2. State-Changing Actions Through Links (CSRF)
Actions that change data — deleting, updating, paying — should only accept POST (or other non-GET methods) and must be protected with CSRF tokens. Code that reads $_REQUEST silently accepts the same parameters via GET as well:
<?php
// Legacy code: deletes a post using $_REQUEST
if (isset($_REQUEST['delete_id'])) {
deletePost((int) $_REQUEST['delete_id']);
}
An attacker can now place a link or an image on another website:
<img src="https://example.com/admin/posts.php?delete_id=42" alt="">
When a logged-in administrator views that page, the browser requests the URL with the administrator's cookies, and the post is deleted. This is a Cross-Site Request Forgery (CSRF) attack. Reading only $_POST (combined with a CSRF token) prevents it, because a simple link or image cannot send a POST request. See CSRF Protection.
3. Cookie Injection
If cookies are included (when request_order is empty and variables_order contains C), a value stored in a cookie can override form data. Cookies can be set by other subdomains or by attackers in some scenarios, so they must never silently replace user input.
4. Harder Testing and Debugging
When a value is wrong, you must check three sources and the server configuration to find out where it came from.
Use the Specific Superglobal Instead
| Data Source | Use |
|---|---|
| URL query string (links, search, filters, pagination) | $_GET |
| Submitted forms (create, update, delete, login) | $_POST |
| Cookies | $_COOKIE |
| JSON request bodies | file_get_contents('php://input') |
Refactoring Legacy Code
<?php
// Before
$id = (int) ($_REQUEST['id'] ?? 0);
// After: this action changes data, so it accepts POST only
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
http_response_code(405);
header('Allow: POST');
exit('Method not allowed');
}
$id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if ($id === false || $id === null) {
http_response_code(400);
exit('Invalid ID');
}
What Changed
- The request method is checked explicitly; other methods receive 405 Method Not Allowed with an
Allowheader naming the accepted method. - The ID is read only from
$_POST, so it cannot be supplied through a link. - The ID is validated as a positive integer instead of being cast blindly — a cast would turn invalid input such as
'abc'into0silently. In a complete application, a CSRF token check would also be added before performing the action.
When Reading Several Sources Is Legitimate
Some endpoints genuinely accept the same parameter from either source — for example, a search page that works both from a link and from a form posted by an older client. Make that explicit instead of relying on $_REQUEST and server configuration:
<?php
function inputValue(string $key, string ...$sources): mixed
{
$arrays = ['get' => $_GET, 'post' => $_POST];
foreach ($sources as $source) {
if (array_key_exists($key, $arrays[$source] ?? [])) {
return $arrays[$source][$key];
}
}
return null;
}
// Request: GET /search.php?q=php+loops
$query = inputValue('q', 'post', 'get');
echo "Search: " . htmlspecialchars((string) $query) . "\n";
Output
Search: php loops
The call inputValue('q', 'post', 'get') documents both the allowed sources and their priority, independent of php.ini. In frameworks, request objects provide similar methods, such as $request->input('q') in Laravel, which document exactly which sources they read.
Checking the Configuration
<?php
echo 'request_order: "' . ini_get('request_order') . '"' . "\n";
echo 'variables_order: "' . ini_get('variables_order') . '"' . "\n";
Output (with the recommended php.ini)
request_order: "GP"
variables_order: "GPCS"
If you maintain an application that relies on $_REQUEST, check these values on every server it runs on, because different configurations change the behavior.
Common Mistakes
Using $_REQUEST for Actions That Change Data
Effect: Actions can be triggered by links, enabling CSRF attacks. Fix: Accept only POST (or PUT/PATCH/DELETE in APIs) and verify CSRF tokens.
Assuming a Specific Override Order
Effect: Different servers produce different values. Fix: Read from $_GET or $_POST explicitly.
Expecting $_REQUEST to Contain Cookies (or Not)
Effect: Behavior changes between environments. Fix: Read cookies from $_COOKIE only.
Modifying $_GET and Expecting $_REQUEST to Change
Effect: $_REQUEST is built once at the start of the request; later changes to $_GET or $_POST are not reflected. Fix: Do not modify superglobals; work with validated copies.
Best Practices
- Avoid
$_REQUESTin new code; use$_GET,$_POST, and$_COOKIEexplicitly. - Make state-changing endpoints POST-only and protect them with CSRF tokens.
- When several sources are allowed, encode the priority in your own code.
- When maintaining legacy code, check
request_orderandvariables_orderon each server. Official reference: https://www.php.net/manual/en/reserved.variables.request.php