What Is `$_SERVER` ?

Yo Yogesh.M Updated 01 Nov 2026
8 min read ·Lesson 214 of 219

In simple terms: $_SERVER is an array with information about the current request and the environment the script runs in: which URL was requested, which HTTP method was used, the visitor's IP address, request headers, the script's file path, and more. Technically: $_SERVER is a superglobal array populated by PHP from the web server (through the server API, such as PHP-FPM or Apache's module) and from the process environment. Its exact contents depend on the web server and its configuration; PHP does not guarantee that every key exists.

Why It Is Needed

Many decisions depend on details of the request itself rather than on submitted data:

  • Was the form submitted (POST) or just displayed (GET)?
  • Which path was requested (for routing)?
  • Is the connection secure (HTTPS)?
  • What language does the browser prefer?
  • From which IP address did the request come (for rate limiting or logging)?

Common Keys

Request Information

KeyExample ValueMeaning
REQUEST_METHODGET, POST, PUT, DELETEHTTP method
REQUEST_URI/products.php?category=books&page=2Path and query string as requested
QUERY_STRINGcategory=books&page=2Only the query string
REQUEST_TIME1791541200Unix timestamp of the request start
REQUEST_TIME_FLOAT1791541200.2531Same, with microseconds
SERVER_PROTOCOLHTTP/1.1Protocol version
HTTPSonNon-empty when the request used HTTPS (may be missing or off otherwise)
CONTENT_TYPEapplication/jsonType of the request body
CONTENT_LENGTH57Size of the request body in bytes

Script and Server Information

KeyExample ValueMeaning
SCRIPT_NAME/products.phpURL path of the running script
SCRIPT_FILENAME/var/www/shop/public/products.phpAbsolute file path of the script
DOCUMENT_ROOT/var/www/shop/publicWeb server document root
PHP_SELF/products.phpScript path plus any extra path info (see Security)
SERVER_NAMEgocourse.inServer name from the web server configuration
SERVER_PORT443Port the server received the request on
SERVER_SOFTWAREnginx/1.26.0Web server name and version

Client and Header Information

KeyExample ValueMeaning
REMOTE_ADDR203.0.113.7IP address of the client (or of the last proxy)
HTTP_HOSTgocourse.inHost header sent by the client
HTTP_USER_AGENTMozilla/5.0 (...)Browser identification
HTTP_ACCEPT_LANGUAGEta-IN,ta;q=0.9,en;q=0.8Preferred languages
HTTP_REFERERhttps://google.com/Page that linked here (often absent)

How Request Headers Become Keys

Every request header is available with the prefix HTTP_, in uppercase, with hyphens replaced by underscores:

Header$_SERVER Key
Accept-LanguageHTTP_ACCEPT_LANGUAGE
X-Requested-WithHTTP_X_REQUESTED_WITH
AuthorizationHTTP_AUTHORIZATION (some servers need extra configuration to pass it)

Content-Type and Content-Length are exceptions: they appear as CONTENT_TYPE and CONTENT_LENGTH, without the prefix.


Reading $_SERVER Safely

Keys may be missing depending on the server, the request, and whether the script runs in the command line. Always provide defaults:

<?php
// Example request: GET https://gocourse.in/products.php?category=books&page=2
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$path = parse_url($uri, PHP_URL_PATH);
$query = $_SERVER['QUERY_STRING'] ?? '';
$isHttps = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
$referrer = $_SERVER['HTTP_REFERER'] ?? 'none';
echo "Method: $method\n";
echo "Path: $path\n";
echo "Query: $query\n";
echo "HTTPS: " . ($isHttps ? 'yes' : 'no') . "\n";
echo "Referrer: $referrer\n";

Output (for the example request, visited directly)

Method: GET
Path: /products.php
Query: category=books&page=2
HTTPS: yes
Referrer: none
  • parse_url(..., PHP_URL_PATH) extracts the path from REQUEST_URI, dropping the query string.
  • The HTTPS check accepts any non-empty value except off, which some servers (such as IIS) use for plain HTTP.
  • HTTP_REFERER is missing when the visitor typed the address, used a bookmark, or the browser's privacy settings removed it.

Detecting the Request Method

The most common use of $_SERVER is distinguishing "show the form" from "process the form":

<?php
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    // validate and process $_POST, then redirect
} else {
    // display the empty form
}

Checking the method is more reliable than checking whether $_POST is empty: a POST request can have an empty body, and a request whose body exceeds post_max_size arrives with an empty $_POST even though it was submitted. See $_POST.


Practical Example: A Small Router

A router maps the method and path of each request to the code that handles it. Keeping the logic in a function makes it testable without a web server:

<?php
function route(string $method, string $uri): array
{
    $path = rtrim(parse_url($uri, PHP_URL_PATH) ?? '/', '/') ?: '/';
    $routes = [
        'GET /' => 'Home page',
        'GET /products' => 'Product list',
        'POST /products' => 'Product created',
    ];
    if (isset($routes["$method $path"])) {
        return [200, $routes["$method $path"]];
    }
    $pathExists = array_filter(
        array_keys($routes),
        fn(string $route): bool => explode(' ', $route, 2)[1] === $path
    );
    return $pathExists ? [405, 'Method not allowed'] : [404, 'Page not found'];
}
$requests = [
    ['GET', '/'],
    ['GET', '/products?page=2'],
    ['POST', '/products/'],
    ['DELETE', '/products'],
    ['GET', '/about'],
];
foreach ($requests as [$method, $uri]) {
    [$status, $body] = route($method, $uri);
    printf("%-6s %-18s → %d %s\n", $method, $uri, $status, $body);
}
// In a real front controller:
// [$status, $body] = route($_SERVER['REQUEST_METHOD'] ?? 'GET', $_SERVER['REQUEST_URI'] ?? '/');
// http_response_code($status);
// echo $body;

Output

GET    /                  → 200 Home page
GET    /products?page=2   → 200 Product list
POST   /products/         → 200 Product created
DELETE /products          → 405 Method not allowed
GET    /about             → 404 Page not found

Step-by-Step Explanation

  1. parse_url() removes the query string, and rtrim(..., '/') removes a trailing slash, so /products/ and /products match the same route. The root path / would become an empty string, so ?: '/' restores it.
  2. Routes are stored as "METHOD /path" keys for fast lookup.
  3. If the path exists but not for this method, the correct HTTP status is 405 Method Not Allowed; if the path does not exist at all, it is 404 Not Found.
  4. The commented lines show how a front controller (public/index.php) feeds $_SERVER values into the function and sends the response. Frameworks use the same principle with far more features: route parameters (/products/{id}), middleware, and controllers.

Security Considerations

Many $_SERVER values come directly from the client and can be manipulated.

PHP_SELF and Cross-Site Scripting

<!-- Vulnerable -->
<form action="<?= $_SERVER['PHP_SELF'] ?>" method="post">

PHP_SELF includes any extra path information after the script name. A request to /contact.php/"><script>alert(1)</script> can make PHP_SELF contain HTML, which is then printed into the page. Safer options:

<form action="" method="post">                                        <!-- submit to the current URL -->
<form action="<?= htmlspecialchars($_SERVER['SCRIPT_NAME']) ?>" method="post">

HTTP_HOST Can Be Spoofed

The Host header is chosen by the client. Using it to build absolute URLs — for example, in password reset emails — allows host header injection: an attacker requests a reset for a victim with a forged Host, and the email contains a link to the attacker's domain. Fix: Build absolute URLs from a configured base URL (for example, APP_URL=https://gocourse.in in your configuration), not from HTTP_HOST. Also configure the web server to accept only your real domain names.

REMOTE_ADDR and Proxies

REMOTE_ADDR is the IP address of whoever connected to the server. Behind a load balancer, CDN, or reverse proxy, that is the proxy's address, and the real client address is passed in a header such as X-Forwarded-For. That header can be forged by anyone, so it may only be trusted when the request actually came from your own proxy:

<?php
function clientIp(array $server, array $trustedProxies): string
{
    $remote = $server['REMOTE_ADDR'] ?? '0.0.0.0';
    if (!in_array($remote, $trustedProxies, true) || empty($server['HTTP_X_FORWARDED_FOR'])) {
        return $remote;
    }
    $chain = array_map('trim', explode(',', $server['HTTP_X_FORWARDED_FOR']));
    foreach (array_reverse($chain) as $ip) {
        if (!in_array($ip, $trustedProxies, true) && filter_var($ip, FILTER_VALIDATE_IP)) {
            return $ip;
        }
    }
    return $remote;
}
$trusted = ['10.0.0.2'];
echo clientIp(['REMOTE_ADDR' => '203.0.113.7'], $trusted) . "\n";
echo clientIp(['REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED_FOR' => '198.51.100.23'], $trusted) . "\n";
echo clientIp(['REMOTE_ADDR' => '203.0.113.7', 'HTTP_X_FORWARDED_FOR' => '1.2.3.4'], $trusted) . "\n";
echo clientIp(['REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED_FOR' => '1.2.3.4, 198.51.100.23'], $trusted) . "\n";

Output

203.0.113.7
198.51.100.23
203.0.113.7
198.51.100.23

How It Works

  1. A direct connection (not from a trusted proxy) returns REMOTE_ADDR; any X-Forwarded-For header sent by the client is ignored (third case).
  2. When the request comes from the trusted proxy, the function reads the forwarded chain from right to left. Each proxy appends the address it received the request from, so the rightmost address not belonging to your proxies is the real client.
  3. In the fourth case, the client tried to fake its address by sending 1.2.3.4 itself; the proxy appended the real address 198.51.100.23, which is the one returned. The function receives the server array as a parameter (clientIp($_SERVER, $trusted) in real code), which makes it easy to test with different inputs.

$_SERVER in the Command Line

When running a script with php script.php, there are no HTTP keys (REQUEST_METHOD, HTTP_HOST, and so on are missing). Instead, $_SERVER['argv'] and $_SERVER['argc'] contain the command-line arguments, and the environment variables of the shell are included. Code that may run in both contexts should use ?? defaults for every HTTP-related key (see PHP Command Line (CLI)).


Common Mistakes

Assuming a Key Always Exists

Effect: Warnings, especially for HTTP_REFERER, HTTPS, and in CLI scripts. Fix: Use ?? with sensible defaults.

Printing PHP_SELF Unescaped

Effect: Cross-site scripting. Fix: Use action="", or escape SCRIPT_NAME with htmlspecialchars().

Effect: Host header injection in emails and redirects. Fix: Use a configured application URL.

Trusting X-Forwarded-For Unconditionally

Effect: Attackers bypass IP-based rate limits or appear to come from any address. Fix: Trust forwarded headers only when REMOTE_ADDR is your own proxy.

Using HTTP_REFERER for Security

Effect: The header is optional and easily forged. Fix: Use CSRF tokens to verify form submissions (see CSRF Protection).


Best Practices

  • Read $_SERVER values with defaults and validate them.
  • Use REQUEST_METHOD to decide between displaying and processing forms.
  • Parse REQUEST_URI with parse_url() for routing.
  • Treat all client-controlled values (HTTP_*, PHP_SELF, REQUEST_URI) as untrusted input.
  • Wrap request-dependent logic in functions that accept values as parameters, so it can be tested without a web server. Official reference: https://www.php.net/manual/en/reserved.variables.server.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings Function Arguments PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PHP XML Parsers PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions What Are Superglobals? $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE
Ask about this post
AI Ask about this post

Ask questions about What Is `$_SERVER` ? and get answers drawn from it.

Signed-in readers only.