nginx中http到https跳转应优先使用$request_uri,因其完整保留原始url编码,避免$uri与$args拼接导致的重复编码、参数损坏等问题;$http_host更贴近原始host头,含端口,适合严格还原场景。

HTTP 到 HTTPS 的跳转本身不涉及 URL 编码(percent-encoding)的主动“解析差异”,但实际转换过程中,编码是否被保留、是否被重复编码、是否因变量拼接出错而损坏,会直接影响跳转后的 URL 是否可访问。Nginx 在处理 $request_uri、$uri、$args 等变量时,对编码的处理逻辑不同,这是关键差异点。
request_uri 保持原始编码,最安全可靠
$request_uri 是 Nginx 接收到的**原始请求行中的完整 URI 字符串**,包括路径和查询参数,且完全保留客户端发送时的百分号编码形式(例如 %E4%BD%A0%E5%A5%BD 不会被解码为“你好”)。它不经过内部 decode,也不做标准化,因此用于 301 跳转时能 100% 还原原始请求。
- ✅ 推荐在 return 或 rewrite 中直接使用:
return 301 https://$http_host$request_uri; - ⚠️ 注意:
$request_uri开头带/,且已含?和参数,无需额外拼接
uri + args 组合易出编码问题
$uri 是解码后的规范化路径(如 /search?q=%E4%BD%A0 → $uri 为 /search),$args 是解码后的参数字符串(q=你好)。若手动拼接 $uri?$args,会导致:
- ❌ 中文等字符被二次编码:浏览器收到
q=%E4%BD%A0%E5%A5%BD后再解码一次,可能变成乱码或 400 错误 - ❌ 特殊符号如
&、=在未转义时破坏参数结构 - ❌ 空格、
#、+等字符行为不一致(尤其在旧版本 Nginx 中)
http_host 与 host 变量对端口和编码无影响,但有格式区别
$http_host 来自请求头 Host: 字段,原样包含端口(如 example.com:8080);$host 是 Nginx 内部标准化后的域名(自动去除端口,按 server_name 匹配,大小写归一)。两者都不做 URL 编码处理,但:
- ✅ 用
$http_host更贴近原始请求,适合严格还原场景 - ⚠️ 若后端 HTTPS 服务监听非标准端口(如 8443),而你用
$host拼https://$host$request_uri,会丢失端口,导致跳转到默认 443 —— 需显式加端口或改用$http_host
rewrite 中的正则捕获需避免隐式 decode
当使用 rewrite ^/(.*)$ https://$host/$1 permanent; 时,Nginx 默认会对捕获组 $1 执行一次 decode(尤其在含中文路径时)。这可能导致:
- ❌ 原始请求为
/产品?id=1(实际是/%E4%BA%A7%E5%93%81?id=1),$1被解成 “产品”,再拼进新 URL 时又被 encode 一次 →/%25E4%25BA%25A7%25E5%2593%2581(双重编码) - ✅ 解决方案:改用
$request_uri,或启用rewrite_log on;结合error_log /path debug;观察实际重写过程











