Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
IP address

How to Get an IP Address Using PHP (Safely, Including Proxies)

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a normal HTTP request, the client address PHP receives is in $_SERVER['REMOTE_ADDR']:

<?php
$ip = $_SERVER['REMOTE_ADDR'] ?? null;

That value is the direct network peer seen by your web server. It is usually the visitor, but it can be a reverse proxy or load balancer. Validate it before storing, displaying or using it in a policy, and read forwarded headers only when your infrastructure has established a trusted proxy boundary.

Read the direct address with REMOTE_ADDR

PHP documents REMOTE_ADDR as “The IP address from which the user is viewing the current page.” The web server supplies entries in $_SERVER; they are not generated by PHP itself. A minimal endpoint is:

<?php
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
echo htmlspecialchars($ip, ENT_QUOTES, 'UTF-8');

htmlspecialchars is appropriate when the value is placed in an HTML response. Even though a correctly formed address contains no HTML characters, treating all request-derived data as untrusted prevents a later change in the data path from becoming an output vulnerability.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Return the value as JSON

<?php
header('Content-Type: application/json; charset=utf-8');

echo json_encode([
    'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
], JSON_UNESCAPED_SLASHES);

This is useful for an internal diagnostic endpoint. Do not expose more server variables than you need; headers and environment values can contain secrets or implementation details.

Validate before you trust or store the value

filter_var with FILTER_VALIDATE_IP accepts valid IPv4 and IPv6 syntax. It returns the original value when valid and false when invalid.

<?php
$raw = $_SERVER['REMOTE_ADDR'] ?? '';
$ip = filter_var($raw, FILTER_VALIDATE_IP) ?: null;

if ($ip === null) {
    http_response_code(400);
    exit('No valid client IP was supplied.');
}

echo htmlspecialchars($ip, ENT_QUOTES, 'UTF-8');

Use the narrower flags when your policy requires them:

  • FILTER_FLAG_IPV4 accepts IPv4 only.
  • FILTER_FLAG_IPV6 accepts IPv6 only.
  • FILTER_FLAG_NO_PRIV_RANGE rejects private address ranges.
  • FILTER_FLAG_NO_RES_RANGE rejects reserved ranges.

For example, this accepts only publicly routable-looking IPv4 values according to PHP’s filter rules:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
$ip = filter_var(
    $_SERVER['REMOTE_ADDR'] ?? '',
    FILTER_VALIDATE_IP,
    FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) ?: null;

Do not automatically reject private addresses in every application. Internal users, tests and private networks legitimately use them. Choose the rule that matches the operation, and document whether IPv6 is supported.

What changes behind a reverse proxy or load balancer?

When a proxy terminates the client connection and opens a new connection to PHP, REMOTE_ADDR identifies that proxy. The proxy may add an X-Forwarded-For header containing the original address and other hops. In PHP, that header appears as $_SERVER['HTTP_X_FORWARDED_FOR'].

A browser can send the same header itself. Therefore, an untrusted request header must never be the sole input for authentication, authorization, rate limiting, an allowlist or an audit decision. Trust it only when the direct peer is one of your configured proxies and that proxy is known to sanitize or replace the header.

A safe decision process

  1. Identify the direct peer from REMOTE_ADDR.
  2. Check whether that exact address (or a range implemented by your framework or network layer) belongs to a trusted proxy you control.
  3. Only for a trusted peer, parse the proxy’s documented forwarded-header format.
  4. Validate every candidate with FILTER_VALIDATE_IP and apply the proxy’s documented left-to-right or right-to-left trust rule.
  5. If the peer is not trusted, ignore forwarded headers and use the validated direct address.

Example with an exact trusted-proxy list

The following complete example is intentionally conservative: it trusts only the exact proxy addresses listed in $trustedProxies. Replace those entries with addresses supplied by your infrastructure team. If your provider publishes CIDR ranges, use your framework’s trusted-proxy facility or a tested CIDR matcher rather than comparing strings.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
function validIp(?string $value): ?string
{
    if ($value === null || $value === '') {
        return null;
    }

    $result = filter_var(trim($value), FILTER_VALIDATE_IP);
    return $result === false ? null : $result;
}

function clientIp(array $server, array $trustedProxies): ?string
{
    $direct = validIp($server['REMOTE_ADDR'] ?? null);
    if ($direct === null) {
        return null;
    }

    // Never inspect forwarding headers from an untrusted peer.
    if (!in_array($direct, $trustedProxies, true)) {
        return $direct;
    }

    $forwarded = $server['HTTP_X_FORWARDED_FOR'] ?? '';
    $parts = $forwarded === '' ? [] : explode(',', $forwarded);
    $addresses = [];

    foreach ($parts as $part) {
        $candidate = validIp($part);
        if ($candidate !== null) {
            $addresses[] = $candidate;
        }
    }

    // Starting at the server side, discard trusted hops. The first
    // untrusted address encountered is the client candidate.
    for ($i = count($addresses) - 1; $i >= 0; $i--) {
        if (!in_array($addresses[$i], $trustedProxies, true)) {
            return $addresses[$i];
        }
    }

    // No usable forwarded client was supplied.
    return $direct;
}

$ip = clientIp(
    $_SERVER,
    ['203.0.113.10', '203.0.113.11'] // example values; configure your real proxies
);

echo htmlspecialchars($ip ?? 'unknown', ENT_QUOTES, 'UTF-8');

This example assumes your proxy appends addresses in a conventional comma-separated chain and that the rightmost entries are closest to your server. Proxy products differ, so follow the format and trust direction documented for your deployment. A framework implementation such as Symfony’s request object follows the same principle: forwarded addresses are considered only after trusted proxies are configured.

REMOTE_ADDR versus X-Forwarded-For

Question REMOTE_ADDR X-Forwarded-For
What it represents The direct TCP peer that connected to the web server A header containing one or more addresses claimed or added by proxies
Works without proxy configuration Yes It may be absent or attacker-controlled
Best use Baseline address and fallback Original-client recovery inside a trusted proxy chain
Main risk It may be the proxy rather than the visitor A client can forge it when the peer is not trusted

HTTP_CLIENT_IP has the same fundamental problem: it is a request header exposed through $_SERVER, not an independently authenticated identity signal. Never “prefer” it merely because it exists.

IPv4, IPv6 and privacy decisions

Do not assume an address contains four dot-separated numbers. FILTER_VALIDATE_IP handles IPv4 and IPv6 syntax, including compressed IPv6 notation. Store addresses in a field sized for IPv6 (at least 45 characters for textual form), or normalize them with a design appropriate to your database.

Choose what your application actually needs

  • Diagnostics: retain the validated address and the timestamp, with an appropriate retention period.
  • Abuse controls: rate-limit a trusted client address, but combine it with account, token or device signals because many people can share one address.
  • Geographic personalization: treat the result as approximate; proxies, VPNs and mobile networks can place it far from the user.
  • Security decisions: do not use an IP as the sole proof of identity.

An IP address is personal or sensitive data in many jurisdictions. Limit access, define retention, and explain the purpose in your privacy documentation. Validation checks syntax; it does not make collection automatically lawful or risk-free.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PHP running from the command line

Normal HTTP server variables are generally unavailable or meaningless when a script runs with the CLI SAPI. This command therefore cannot reveal the address of a web visitor:

php script.php

In CLI code, check the execution mode and require an explicit value instead:

<?php
if (PHP_SAPI === 'cli') {
    fwrite(STDERR, "No HTTP client address exists in CLI mode.n");
    exit(1);
}

$ip = filter_var($_SERVER['REMOTE_ADDR'] ?? '', FILTER_VALIDATE_IP) ?: null;

Testing an endpoint

Create a file such as ip.php under your web root, serve it through the same proxy path used in production, and request it from a browser. Testing through the proxy is important: a direct local PHP server and a production load balancer can produce different REMOTE_ADDR values.

Test with cURL

curl -i https://example.com/ip.php

Do not add a hand-written X-Forwarded-For header and conclude that it is trustworthy; that only tests your application’s parsing branch, not your proxy boundary.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Call the endpoint from Python

import requests

response = requests.get("https://example.com/ip.php", timeout=15)
response.raise_for_status()
print(response.text)

Call the endpoint from Node.js

const res = await fetch('https://example.com/ip.php');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log(await res.text());

These clients are only making requests to your PHP endpoint. The address PHP sees is determined by the network path between the client, proxy and server.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common results

You always see the load balancer’s address

That is expected when the balancer terminates the connection and does not pass a trusted forwarded value. Confirm which header your provider sets, configure the application’s trusted proxy addresses, and preserve REMOTE_ADDR as the fallback.

You see a different address on every request

Mobile carriers, corporate gateways, VPNs and IPv6 privacy addresses can change over time. An IP is not a stable user identifier. Log request time and relevant application identifiers rather than attempting to “fix” a changing address.

X-Forwarded-For contains several values

That list represents hops, not a guarantee that the leftmost value is genuine. Validate each value and use the trust direction specified by your proxy. If you cannot establish that boundary, ignore the list.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The value is empty in a script

Check whether the script is running under CLI, a worker, a test harness or a server configuration that does not populate the variable. HTTP client variables are not guaranteed outside a normal web request.

A filter rejects an address you expected to allow

Check whether you enabled IPv4-only, private-range or reserved-range flags. Remove only the flag that conflicts with your documented policy; do not disable validation altogether.

Or skip the browser setup

If your wider workflow needs clean captures of a page rather than a PHP diagnostic endpoint, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP or PDF output:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for options and response details. Before capture it accepts consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Operational checklist

  • Use REMOTE_ADDR as the direct-peer baseline.
  • Validate with FILTER_VALIDATE_IP before storage or output.
  • Configure trusted proxies explicitly; never trust a header because it is present.
  • Support IPv6 unless your documented requirement is IPv4-only.
  • Escape values for their output context and protect logs from unnecessary exposure.
  • Test through the real proxy path and include an explicit fallback when forwarding data is missing or malformed.

Frequently Asked Questions

Can PHP discover a visitor’s public home IP if the visitor uses a VPN?

No. PHP receives the address presented by the network path, which may be a VPN, corporate gateway, mobile carrier or proxy. It cannot reliably reveal an underlying address that the network has hidden.

Should I save the raw X-Forwarded-For string for auditing?

Only if you have a defined privacy and logging purpose. If you do save it, treat it as untrusted input, restrict access and retain it for no longer than necessary; do not treat it as an authenticated identity record.

Is an IP address enough to identify one person?

No. Shared networks, changing mobile addresses, VPNs and privacy mechanisms mean an address can represent many people or change users over time.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.