$_server['request_uri']包含完整请求路径和查询字符串(如/user/profile?id=123),但不含协议和域名,且可能不以/开头;$_server['php_self']仅返回当前脚本路径(如/index.php),无查询参数,且易受重写规则干扰;而$_server['script_name']由服务器内部生成,稳定可靠,始终指向真实执行脚本路径。

PHP中$_SERVER['REQUEST_URI']和$_SERVER['PHP_SELF']的区别
想拼出当前完整URL,光靠$_SERVER['REQUEST_URI']或$_SERVER['PHP_SELF']都不够——前者不含协议和域名,后者不带查询参数且可能被重写规则干扰。
常见错误是直接拼接:$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],但没处理HTTP_X_FORWARDED_PROTO(反向代理场景下HTTPS会被识别成HTTP),也没过滤空值。
-
$_SERVER['REQUEST_URI']包含路径+查询字符串(如/user/profile?id=123),但不保证以/开头(某些CGI环境会缺失) -
$_SERVER['PHP_SELF']只返回当前脚本路径(如/index.php),不包含QUERY_STRING,且在Apache mod_rewrite后可能被篡改 - 必须检查
$_SERVER['HTTPS'] === 'on'或$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'来判断协议,否则Nginx+PHP-FPM部署时总生成http://链接
安全拼接当前完整URL的可靠写法
不能依赖单一$_SERVER字段,要组合校验。以下逻辑覆盖绝大多数部署环境(Apache/Nginx + CLI/CGI/FPM):
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ||
(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
$uri = $_SERVER['REQUEST_URI'] ?? '/';
// 确保REQUEST_URI以/开头
if (strpos($uri, '/') !== 0) {
$uri = '/' . $uri;
}
$current_url = $protocol . '://' . $host . $uri;
注意:$_SERVER['HTTP_HOST']可被客户端伪造,若用于安全敏感场景(如OAuth回调校验),需白名单过滤$host;仅作前端跳转展示则无需额外处理。
为什么$_SERVER['SCRIPT_NAME']比PHP_SELF更稳定
$_SERVER['SCRIPT_NAME']由服务器内部生成,不受mod_rewrite或try_files重写影响,而$_SERVER['PHP_SELF']在URL重写后可能返回重写前的原始路径(比如访问/user/123却得到/index.php)。
- 需要获取“当前执行脚本的真实路径”时(如生成表单action),优先用
$_SERVER['SCRIPT_NAME'] -
$_SERVER['PHP_SELF']只在未启用重写、且无恶意PATH_INFO注入风险时才相对安全 - 如果脚本在子目录(如
https://a.com/app/index.php),SCRIPT_NAME返回/app/index.php,而PHP_SELF可能被污染为/app/index.php/some/path
CLI环境下$_SERVER变量不可用的替代方案
命令行运行PHP时$_SERVER['REQUEST_URI']等全为空,此时无法构造HTTP URL。若脚本需同时支持Web和CLI(如定时任务触发页面抓取),必须显式传入基础URL:
- 通过配置文件定义
$base_url = 'https://example.com';,Web和CLI共用 - CLI调用时加参数:
php script.php --url=https://example.com/user,再解析$argv - 绝对不要在CLI中硬编码
$_SERVER字段并期望它存在——会直接触发Notice甚至Fatal Error
最易被忽略的是:本地开发用内置服务器(php -S)时,$_SERVER['HTTPS']永远为空,必须靠$_SERVER['SERVER_PORT'] === '443'辅助判断,但该端口也可能被非HTTPS服务占用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











