安全获取当前完整URL需先判断协议(检查HTTPS和HTTP_X_FORWARDED_PROTO),再拼接HTTP_HOST与REQUEST_URI;修改参数应基于$_GET数组用http_build_query()重建,禁用PHP_SELF或手动字符串拼接。

PHP 怎么安全获取当前完整 URL(含协议、域名、路径、查询参数)
直接拼接 $_SERVER['HTTP_HOST'] 和 $_SERVER['REQUEST_URI'] 是常见做法,但容易忽略 HTTPS 判断和端口异常,导致生成的 URL 错误(比如 http://example.com:443/xxx 这种不规范地址)。
- 必须用
$_SERVER['HTTPS']或$_SERVER['SERVER_PORT']判断协议,不能只看端口是否为 443 -
$_SERVER['REQUEST_URI']已包含查询字符串(?a=1&b=2),无需再手动拼$_GET - 如果站点用了反向代理(如 Nginx + PHP-FPM),
$_SERVER['HTTPS']可能为空,需检查$_SERVER['HTTP_X_FORWARDED_PROTO']
$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'];
$request_uri = $_SERVER['REQUEST_URI'];
$current_url = $protocol . '://' . $host . $request_uri;
如何在保留原参数基础上追加或修改某个 URL 参数
直接操作 $_SERVER['QUERY_STRING'] 容易出错(编码、重复键、空值处理),推荐用 http_build_query() 重建整个查询串。
- 先用
$_GET获取全部参数,再按需修改键值(比如覆盖page) - 注意:
http_build_query()默认对键和值做urlencode(),无需手动处理 - 若要删除某个参数,用
unset($_GET['xxx'])再重建
$params = $_GET; $params['page'] = 5; // 覆盖 page 参数 $new_query = http_build_query($params); $current_url_without_query = strtok($_SERVER['REQUEST_URI'], '?'); $full_url_with_new_params = $current_url_without_query . '?' . $new_query;
为什么不要用 $_SERVER['PHP_SELF'] 拼 URL
$_SERVER['PHP_SELF'] 只返回脚本路径(如 /index.php),不含查询参数,也不反映重写后的 URL(如 Apache 的 RewriteRule),更不包含协议和域名。强行拼接会丢失关键信息,且在 URL 重写场景下完全失效。
- 它可能被恶意构造(如
/index.php/some/path?x=1中的/some/path会被包含),存在安全风险 - 与
$_SERVER['REQUEST_URI']相比,缺少查询字符串,无法还原原始请求 - 在 CLI 或某些 SAPI 环境下该变量可能不存在或为空
URL 参数拼接时最容易踩的坑
多数问题出在“以为自己在拼参数,其实已经破坏了原有结构”。比如手动字符串拼接 ? 和 &,却没考虑原始 URL 是否已有参数。
立即学习“PHP免费学习笔记(深入)”;
- 原始 URL 是
/search?q=php&sort=date,再加page=2时,错误写法:?page=2→ 结果变成/search?page=2(丢失所有原参数) - 正确做法永远基于
$_GET数组重建,而不是解析字符串 - 如果页面本身无查询参数(
$_SERVER['QUERY_STRING']为空),http_build_query([])返回空字符串,此时不应加? - 中文或特殊字符参数必须依赖
http_build_query()自动编码,手写urlencode()容易漏掉键名
REQUEST_URI 的影响——这两处一错,整个 URL 构造就不可信。











