URL重写中中文参数乱码本质是rewrite破坏原始UTF-8编码字节完整性:Nginx默认解码URI后匹配,若拼接或跳转未保持字节原样,易致二次解码错位;应优先用$request_uri保留原始编码,避免变量拼接引发歧义,并确保后端不重复解码。

URL 重写中中文参数乱码,本质是重写过程破坏了原始 UTF-8 编码字节的完整性。Nginx 的 rewrite 指令默认对 URI 进行一次解码后再匹配和重写,若后续拼接或跳转未保持字节原样,就容易触发二次解码或编码错位,导致乱码或 404。
确保原始请求已正确 UTF-8 编码
浏览器发送含中文的 URL(如 /用户/头像.jpg)时,会自动转为 /%E7%94%A8%E6%88%B7/%E5%A4%B4%E5%83%8F.jpg。这是标准行为,但前提是:
- 不要在地址栏手动输入裸中文后直接回车(部分旧版 curl 或调试工具可能用系统 locale 编码,比如 GBK)
- 用
curl -v "http://host/%E7%94%A8%E6%88%B7/"验证,确保请求中确实是合法 UTF-8 百分号编码 - 检查 Nginx access_log 中的
$request_uri变量,确认收到的是原始编码串,而非已解码乱码
rewrite 规则中避免隐式解码/再编码
rewrite 指令作用于已解码后的 $uri(即字节序列),不是原始 $request_uri。这意味着:
- 若原始路径是
/%E7%94%A8%E6%88%B7/,Nginx 解码后得到 UTF-8 字节用户/,rewrite ^/(.*)$ /api/$1 break;实际拼的是这些字节 - 但若目标 location 后接 proxy_pass 或内部跳转,而 proxy_pass 地址又含变量拼接(如
proxy_pass http://backend$uri;),就可能把 UTF-8 字节当字符串再次参与 URL 构造,引发歧义 - 推荐写法:用
rewrite ... redirect时,目标地址尽量静态;若需动态,改用return 302 /api/$request_uri;,保留原始编码
location 匹配与重定向响应头需协同
重定向返回 302 响应时,Location 响应头值必须是合法可被浏览器解析的 URI:
- 如果重写后生成的跳转地址含裸中文(如
Location: /用户/),部分客户端会拒绝处理或自行按 Latin-1 解码,显示乱码 - 正确做法是让重定向地址也保持 UTF-8 百分号编码:用
return 302 /api/$request_uri;或在 rewrite 中用encode标志(OpenResty 支持,原生 Nginx 不支持) - 若必须用 rewrite + redirect,确保源 URI 已是标准编码,且目标路径不引入新中文字符(例如统一转为 ASCII 别名:
rewrite ^/用户/(.*)$ /u-$1 permanent;)
后端接收时别再“多解一次”
即使 Nginx 重定向正确,后端若对已解码路径再次尝试解码(比如 Python 的 urllib.parse.unquote 作用于已被 Nginx 解码过的 $uri),也会出错:
- 检查后端框架是否自动处理 path 参数;建议直接读取原始请求路径字段(如 WSGI 的
environ['PATH_INFO']),它已是解码后 UTF-8 字符串 - 避免在 Nginx 层用
set+proxy_set_header拼接中文路径传给后端,这极易因变量扩展时机导致字节污染 - 调试时可在 Nginx 中加日志:
log_format debug '$request_uri → $uri → $args';,对比三者差异定位解码环节


















