What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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.
#1 Best Overall
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_IPV4accepts IPv4 only.FILTER_FLAG_IPV6accepts IPv6 only.FILTER_FLAG_NO_PRIV_RANGErejects private address ranges.FILTER_FLAG_NO_RES_RANGErejects reserved ranges.
For example, this accepts only publicly routable-looking IPv4 values according to PHP’s filter rules:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall<?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.
Rank #2
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
- Identify the direct peer from
REMOTE_ADDR. - Check whether that exact address (or a range implemented by your framework or network layer) belongs to a trusted proxy you control.
- Only for a trusted peer, parse the proxy’s documented forwarded-header format.
- Validate every candidate with
FILTER_VALIDATE_IPand apply the proxy’s documented left-to-right or right-to-left trust rule. - 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.
<?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.
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.
Rank #4
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.
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.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.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsThe 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.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Operational checklist
- Use
REMOTE_ADDRas the direct-peer baseline. - Validate with
FILTER_VALIDATE_IPbefore 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.
Quick Recap
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.




