HOWTO · PHP
How to Get User IP Address in PHP
This tutorial demonstrates how to get the user's IP address in PHP
On this page
To read the network address associated with a PHP request, start with $_SERVER['REMOTE_ADDR']. It identifies the peer that connected to the web server. If a reverse proxy sits in front of the application, that peer may be the proxy rather than the visitor. Proxy-related request headers are usable only when your application has an explicit trust configuration for the proxy that supplies and validates them.
Read the connection address with $_SERVER['REMOTE_ADDR']
$_SERVER is a PHP superglobal containing server and request information. Its REMOTE_ADDR element holds the address of the peer connected to the server handling the request. On a direct connection, that will usually be the connecting client. Behind a load balancer or reverse proxy, it will commonly be the proxy’s address. The web server and proxy configuration determine what PHP receives, so this value should not be described as a guaranteed end-user address.
For a local request, the peer can be the IPv6 loopback address ::1. Loopback addresses refer to the local machine; ::1 is the IPv6 loopback address, corresponding to IPv4’s 127.0.0.1. The exact value depends on the local server and connection. See this loopback address explanation.
The following runnable example sets a representative server value so the lookup is self-contained. It keeps display_output set to false, so the example performs the lookup without emitting display output; in an application, use the value according to your own response or logging needs.
<?php
$_SERVER['REMOTE_ADDR'] = '::1';
$peerAddress = $_SERVER['REMOTE_ADDR'];
$display_output = false;
if ($display_output) {
echo "The connected peer is: " . $peerAddress . PHP_EOL;
}
?>
For a real request, use the server-provided $_SERVER['REMOTE_ADDR'] value rather than assigning a sample value. The PHP Manual’s server variables reference describes the available $_SERVER entries.
Interpret proxy headers only when the proxy is trusted
PHP may expose request headers as $_SERVER entries such as HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP. Their presence does not prove that they contain a visitor’s address: a client can send header values of its own, and a proxy may preserve, append, or replace them. In particular, HTTP_X_FORWARDED_FOR can contain a comma-separated chain, not just one address. Do not select the first value and call it the real client address without applying your infrastructure’s documented trust rules.
Use a forwarded value only when the connection peer matches a proxy your application explicitly trusts and that proxy is configured to remove or validate client-supplied forwarding headers. The trusted proxy list must match your deployment; do not copy sample addresses as production settings. If the peer is not trusted, use REMOTE_ADDR. HTTP_CLIENT_IP has the same trust problem and should not be used as a special answer for shared internet connections.
This runnable snippet models a deployment where the trusted proxy overwrites X-Forwarded-For with one validated address. Replace the sample proxy address and validation policy with the values defined for your infrastructure. It prints the selected forwarded address for these representative values; it does not implement parsing for a multi-proxy chain.
<?php
$_SERVER['REMOTE_ADDR'] = '192.0.2.10';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '198.51.100.24';
$trustedProxies = ['192.0.2.10'];
$peerAddress = $_SERVER['REMOTE_ADDR'];
if (in_array($peerAddress, $trustedProxies, true)
&& isset($_SERVER['HTTP_X_FORWARDED_FOR'])
&& filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP) !== false) {
$clientAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$clientAddress = $peerAddress;
}
echo "Selected address: " . $clientAddress . PHP_EOL;
?>
Selected address: 198.51.100.24
The documentation for filter_var() explains the validation function used here. Validating that a string is an IP address does not establish that it came from a trusted source; the peer check and proxy configuration provide that trust decision. If your proxy forwards a chain, use a framework or carefully reviewed code that walks the chain according to the exact trusted-proxy rules for your deployment.
Use a conditional expression for a compact fallback
A readable if/elseif/else block is usually easier to audit, especially when it includes a proxy trust check. A conditional expression can express the same choice compactly, but it does not make an untrusted header safe. The following runnable example uses a trusted-proxy check and the same single-address, overwrite policy described above. Its server values are representative; it prints the selected forwarded address.
<?php
$_SERVER['REMOTE_ADDR'] = '192.0.2.10';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '198.51.100.24';
$trustedProxies = ['192.0.2.10'];
$peerAddress = $_SERVER['REMOTE_ADDR'];
$forwardedAddress = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$clientAddress = in_array($peerAddress, $trustedProxies, true)
&& filter_var($forwardedAddress, FILTER_VALIDATE_IP) !== false
? $forwardedAddress
: $peerAddress;
echo "Selected address: " . $clientAddress . PHP_EOL;
?>
Selected address: 198.51.100.24
The fallback order is deliberate: accept the validated forwarded address only when the connected peer is on the trusted proxy list; otherwise retain the connected peer address. Parentheses can make nested conditional expressions clearer, but a nested ternary is harder to maintain and does not replace the trust checks. Keep the proxy’s header behavior and the application’s allowlist aligned so a client cannot promote its own header to trusted input.