Superglobals
PHP fills a set of arrays called superglobals before your script starts. Unlike ordinary variables, they are visible in every function without global:
| Superglobal | Holds |
|---|---|
$_GET |
query string parameters |
$_POST |
form fields sent with POST |
$_COOKIE |
cookies sent by the browser (Cookies) |
$_SESSION |
session data, after session_start() (Sessions) |
$_FILES |
uploaded files (File uploads) |
$_SERVER |
details about the request and the server |
$_ENV |
environment variables (often empty; use getenv()) |
Reading the request from $_SERVER
$_SERVER describes the current request. The keys you will use most:
<?php
header('Content-Type: text/plain; charset=utf-8');
echo "Method: " . $_SERVER['REQUEST_METHOD'] . "\n";
echo "URI: " . $_SERVER['REQUEST_URI'] . "\n";
echo "Path: " . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . "\n";
echo "Query: " . ($_SERVER['QUERY_STRING'] ?? '') . "\n";
echo "Accept: " . ($_SERVER['HTTP_ACCEPT'] ?? 'not sent') . "\n";
echo "Language: " . ($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'not sent') . "\n";GET /request.php?sort=price&page=2
Accept: text/html
Accept-Language: en-INMethod: GET
URI: /request.php?sort=price&page=2
Path: /request.php
Query: sort=price&page=2
Accept: text/html
Language: en-INEvery request header appears as HTTP_ plus its name in capitals, with dashes turned into underscores. Treat all of these as user input: the browser, or anyone else, can send any value. That includes HTTP_HOST, HTTP_REFERER and HTTP_USER_AGENT. REMOTE_ADDR holds the visitor's IP address as seen by the server; behind a proxy or CDN it is the proxy's address.
Status codes and headers
An HTTP response has a status code, then headers, then the body. PHP sends 200 OK and a text/html content type unless you say otherwise. http_response_code() sets the status and header() adds or replaces a header:
<?php
$products = [1 => 'Mechanical Keyboard', 2 => 'Ceramic Mug Set'];
$id = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT);
if ($id === false || !isset($products[$id])) {
http_response_code(404);
echo "<h1>Product not found</h1>";
exit;
}
echo "<h1>" . htmlspecialchars($products[$id]) . "</h1>";GET /product.php?id=7HTTP/1.1 404 Not Found
<h1>Product not found</h1>Returning a real 404 for a missing page matters: search engines drop such URLs instead of indexing an error page as if it were content.
Headers already sent
Headers travel before the body. As soon as your script prints anything, even a space or a blank line before <?php, PHP sends the headers, and it is too late to add more:
<?php
echo "Hello";
header('Location: /elsewhere.php');
echo "\n";Hello
Warning: Cannot modify header information - headers already sent by (output started at /path/to/late.php:2) in /path/to/late.php on line 3The message tells you where the output started, which is where to look. The usual culprits are whitespace before <?php, a closing ?> followed by a blank line in an included file (which is why PHP-only files leave out the closing tag), or a debugging echo. Do all header work, redirects and cookies at the top of the script, before any HTML.
Redirects
A redirect tells the browser to load another URL. Send a Location header with a 3xx status, then stop the script with exit: header() only adds a header and does not end anything by itself.
<?php
header('Location: /new-page.php', true, 301);
exit;GET /old-page.phpHTTP/1.1 301 Moved Permanently
Location: /new-page.phpUse 301 when a page has moved for good (search engines transfer its ranking), 302 or 303 for a temporary redirect, such as after a form.
Post/Redirect/Get
If a page that just processed a POST shows its result directly, refreshing the page makes the browser send the POST again: the order is placed twice, the message sent twice. The fix is the Post/Redirect/Get pattern: process the POST, then redirect to a normal GET page that shows the result.
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$orderId = 1043; // save the order here and get its id
header('Location: /thanks.php?order=' . $orderId, true, 303);
exit;
}
echo '<form method="post"><button>Place order</button></form>';<?php
$order = filter_var($_GET['order'] ?? '', FILTER_VALIDATE_INT);
echo "<p>Thank you! Your order number is " . ($order ?: 'unknown') . ".</p>";POST /order.php
Content-Type: application/x-www-form-urlencoded
confirm=1HTTP/1.1 303 See Other
Location: /thanks.php?order=1043
The browser then requests /thanks.php?order=1043, and refreshing that page is harmless. Status 303 See Other explicitly tells the browser to follow up with a GET.
Returning JSON
PHP pages can just as easily answer with JSON, for JavaScript on your own site or for other programs. Set the content type, then print json_encode():
<?php
header('Content-Type: application/json');
$product = ['id' => 1, 'name' => 'Mechanical Keyboard', 'price' => 89.5, 'inStock' => true];
echo json_encode($product);GET /api-product.phpHTTP/1.1 200 OK
Content-Type: application/json
{"id":1,"name":"Mechanical Keyboard","price":89.5,"inStock":true}JSON is covered fully in JSON: encoding and decoding data.
Common mistakes
- Output before header(), including whitespace before
<?phpor after?>in an included file. - Forgetting exit after a Location header, so the rest of the script still runs (and may do something it should not).
- Showing the result of a POST directly instead of redirecting.
- Trusting $_SERVER values that come from request headers, such as the referrer.
Exercise
Write go.php that redirects ?to=learn to /learn/ and ?to=tools to /tools/ with a 302, and answers anything else with a 404 and the text "Unknown destination". Never put the raw to value into the Location header.
Show one possible answer
<?php
$targets = ['learn' => '/learn/', 'tools' => '/tools/'];
$to = $_GET['to'] ?? '';
if (!isset($targets[$to])) {
http_response_code(404);
echo "Unknown destination";
exit;
}
header('Location: ' . $targets[$to], true, 302);
exit;GET /go.php?to=toolsHTTP/1.1 302 Found
Location: /tools/Redirecting only to URLs from your own list avoids an open redirect, where attackers use your domain to send people to a phishing site.
Key points
- Superglobals such as $_SERVER, $_GET, $_POST, $_COOKIE, $_SESSION and $_FILES are available everywhere.
- $_SERVER['REQUEST_METHOD'] and $_SERVER['REQUEST_URI'] describe the request.
- header() and http_response_code() must be called before any output, or PHP reports 'headers already sent'.
- After a successful POST, redirect with a 303 to a GET page (Post/Redirect/Get) and call exit.