Nginx变量不参与编解码,但其值编码状态各异:$request_uri保持原始编码,$uri和$args已解码;拼接时易致双重编码,应优先用$request_uri跳转;日志转义需按需配置escape参数;正则匹配基于解码后字符串。

Nginx 中的变量本身不参与 URL 编码或解码,但变量所承载的值(如 $request_uri、$uri、$args)在不同阶段具有明确的编码状态,理解这点是避免乱码、400 错误和跳转损坏的关键。
变量值的编码状态各不相同
Nginx 内部对不同变量做了不同处理:
-
$request_uri:原始接收的 URI 字符串,含完整百分号编码(如/测试.pdf?name=%E4%BD%A0),不做任何解码,最适合作为 301/302 跳转目标 -
$uri:已解码的规范化路径部分(如/测试.pdf),不含查询参数,用于 location 匹配和文件查找 -
$args:已解码的查询参数字符串(如name=你),单独使用时需注意拼接风险 -
$query_string:等价于$args,语义更清晰,推荐在日志或重写中使用
手动拼接变量极易引发双重编码
用 $uri 和 $args 拼出完整 URL 是常见陷阱:
- 错误写法:
return 301 https://$host$uri?$args;→ 中文参数可能被二次 encode 成%25E4%25BD%25A0 - 正确做法:
return 301 https://$http_host$request_uri;→ 完整保留原始编码 - 若需修改 host 或协议,应先用
rewrite捕获再构造,避免隐式 decode
日志与响应中的转义控制
日志输出默认会对非 ASCII 字符做 HTML 实体转义(如 中文 → 中文),影响日志分析:
- 启用 JSON 日志可规避此问题:
log_format main_json escape=json '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'; - 若用
echo或return输出变量内容,Nginx 不自动转义,但浏览器解析仍依赖响应头 charset(建议设为charset utf-8;) -
escape=default是 log_format 默认行为;escape=none可禁用转义,但需确保下游系统能安全处理原始字节
正则匹配必须针对解码后的字符串
location 正则表达式运行在 URI 解码之后的路径上:
- 请求
/report_v1[draft].pdf(实际发送为/report_v1[draft].pdf)→ Nginx 解码为/report_v1[draft].pdf→ 正则需写成location ~ \[.*\]\.pdf$ - 避免用未转义的元字符:
[、]、.、+、?在正则中必须加反斜杠 - 更稳健方案:用前缀匹配 +
try_files,绕开正则解析复杂度


















