$_SERVER['SERVER_ADDR']与$_SERVER['SERVER_PORT']组合可得本机监听的IP+端口,如$host = $_SERVER['SERVER_ADDR'] . ':' . $_SERVER['SERVER_PORT'];但该值仅反映服务器绑定地址,非公网地址,且受部署环境(如代理、Docker、NAT)影响,不可靠;推荐显式配置对外地址。

PHP 里 $_SERVER['SERVER_ADDR'] 和 $_SERVER['SERVER_PORT'] 是最直接的组合
PHP 本身不提供“带端口的本机 IP 字符串”这种现成变量,但 $_SERVER['SERVER_ADDR'] 给出的是 Web 服务器监听的 IP(如 127.0.0.1 或 192.168.1.100),$_SERVER['SERVER_PORT'] 给出的是当前请求所用的端口(如 80、443、8080)。两者拼接就是你要的“IP+端口”:
$host = $_SERVER['SERVER_ADDR'] . ':' . $_SERVER['SERVER_PORT'];
注意:$_SERVER['SERVER_ADDR'] 不等于客户端看到的公网 IP,它只反映 PHP 所在服务器网卡上实际绑定的地址;如果用了反向代理(Nginx / Apache proxy),这个值仍是后端 PHP 服务监听的内网地址,不是代理层暴露的地址。
为什么不能用 $_SERVER['HTTP_HOST'] 或 $_SERVER['SERVER_NAME']?
这两个变量容易被客户端篡改或受配置影响,不可靠:
-
$_SERVER['HTTP_HOST']来自请求头,可被伪造(比如 curl -H "Host: evil.com") -
$_SERVER['SERVER_NAME']取决于 Web 服务器配置(如 Apache 的ServerName),未必和监听地址一致,甚至可能为空 - 它们都**不保证包含端口**:HTTP/HTTPS 默认端口(80/443)通常被省略,
$_SERVER['HTTP_HOST']在非默认端口下才带端口,但逻辑不可控
分离已有字符串中的 IP 和端口(如 "192.168.1.5:3000")
用 parse_url() 不适用(它面向 URL,不是裸 IP:port),推荐用 explode() + 简单校验:
立即学习“PHP免费学习笔记(深入)”;
$input = '192.168.1.5:3000';
$parts = explode(':', $input, 2); // 最多切两段,防 IPv6 地址冒号干扰
$ip = $parts[0] ?? '';
$port = $parts[1] ?? null;
// 基础校验(可选)
if ($port !== null && !is_numeric($port) && $port < 1 || $port > 65535) {
$port = null;
}
注意:IPv6 地址如 [::1]:8080 需要先去掉方括号再处理,否则 explode 会错切。真实场景中若需兼容 IPv6,建议用正则或 parse_url('tcp://' . $input) 辅助提取。
获取“对外可见”的本机地址?没有通用解,得看部署环境
PHP 运行时无法自动知道“外网用户访问你时用的是哪个 IP 和端口”,因为这取决于网络拓扑:
- 本地开发:
$_SERVER['SERVER_ADDR']+$_SERVER['SERVER_PORT']基本可用 - 云主机/NAT 后:
$_SERVER['SERVER_ADDR']是内网 IP,公网 IP 得查服务商 API 或配置文件 - Docker 容器:
$_SERVER['SERVER_ADDR']通常是容器内网 IP(如172.17.0.2),宿主机映射端口需额外传入环境变量(如APP_PORT=8080) - 反向代理后:端口往往是代理监听的(如 443),但 PHP 收到的
$_SERVER['SERVER_PORT']可能仍是后端的 80/8000 —— 此时应优先信任代理设置的X-Forwarded-Port(需手动启用并校验来源)
真正稳定的方案是:把“对外访问地址”作为配置项显式注入,而不是试图让 PHP 自己猜。











