Nginx 不解码 URI,需用 UTF-8 百分号编码(如 /%E6%96%87%E7%AB%A0/)写 location 规则;推荐 ^~ 前缀匹配或交由后端解码处理。

Nginx 处理带中文的 URL 路径时,核心原则是:不主动解码 URI,而是按客户端发送的原始字节(通常是 UTF-8 编码后的 %-encoding 形式)进行匹配。这意味着你写 location 规则时,不能直接写中文字符,而应基于浏览器实际发出的编码格式来设计匹配逻辑。
中文路径在 URL 中的真实样子
浏览器访问 https://example.com/文章/ 时,会自动将中文转为百分号编码:
→ 实际请求 URI 是 /%E6%96%87%E7%AB%A0/
同理,/测试.html → /%E6%B5%8B%E8%AF%95.html
Nginx 默认不对 URI 做解码再匹配,它直接拿这个 /E6%96%87... 字符串去比对 location 规则。
location 怎么写才能匹配中文路径?
✅ 推荐方式:用 ^~ 或无修饰符做前缀匹配(最稳妥)
location ^~ /%E6%96%87%E7%AB%A0/ {
root /var/www/html;
}或更通用一点(匹配所有以中文路径开头的请求):
location ^~ /% {
# 拦截所有含 % 编码的路径,再交由后端或 rewrite 处理
return 400 "Chinese path not allowed";
}⚠️ 注意:
%E6%96%87%E7%AB%A0是 UTF-8 编码结果,不同系统/浏览器编码一致,可放心用。
✅ 替代方案:用正则匹配编码段(需注意大小写和转义)
# 匹配任意 UTF-8 中文编码(3字节常见,如 %E4%BD%A0)
location ~ ^/%(?:[A-Fa-f0-9]{2}){3,}/? {
proxy_pass http://backend;
}说明:
-
%后跟两个十六进制字符为一个字节; - 中文 UTF-8 通常占 3 字节(如“你”=
%E4%BD%A0),所以{3,}表示至少匹配 3 个字节(即一个中文起); -
^/确保从路径开头匹配; - 正则中
%要原样写,不需要\%(除非后面紧跟字母数字,否则会被误识别为转义)。
❌ 不推荐:直接写中文字符(如 location /文章/)
Nginx 配置文件若保存为 UTF-8 编码,某些旧版本可能解析失败;
即使能读,Nginx 也不会把 /文章/ 自动转成 /%E6%96%87%E7%AB%A0/ 去匹配——它只做字面匹配,而请求里是编码后的字符串,必然不匹配。
如果希望「让 Nginx 按解码后的内容匹配」怎么办?
Nginx 本身不提供 URI 解码后再匹配的功能。但可通过间接方式实现:
用
rewrite先解码再跳转(不推荐,有风险)
实际不可行:rewrite不支持对$uri解码,且解码可能引入安全问题(如%/00/../etc/passwd)。-
交给后端处理(推荐)
把带中文的请求统一代理出去,由应用层(如 Python/Java)负责 decode 和路由:location / { proxy_pass http://app_server; proxy_set_header X-Original-URI $request_uri; } -
统一重写为英文别名(实用做法)
例如把/文章/→/article/,用map或rewrite提前转换:map $request_uri $mapped_uri { ~^/文章/(.*)$ /article/$1; ~^/图片/(.*)$ /images/$1; default $request_uri; } server { location / { rewrite ^ $mapped_uri break; proxy_pass http://backend; } }
小心这些坑
- 请求中出现空格 → 编码为
%20,不是+(表单 enctype=application/x-www-form-urlencoded 才用+,但 location 匹配的是原始 URI,不是 query string); -
location = /文章/永远不会命中,因为真实请求是/E6%96%87.../; -
location /%E6/可以匹配/E6%96%87/(前缀匹配),但location ~ /E6/也能匹配,只是优先级低于=和^~; - 使用
try_files时,$uri是未解码的,所以try_files $uri /index.html;对/文章.html会去找磁盘上名为%E6%96%87.html的文件,不是文章.html。
不复杂但容易忽略


















