浏览器网址默认将unicode字符编码为%##。
但是,我可以通过CURL向http://localhost:8080/与
发出请求,nginx将路径视为“与
”。这怎么可能? Nginx是否允许在其路径中使用任意unicode? p>
例如,使用此配置我可以设置一个额外的标头来查看nginx看到了什么:
location ~* "(*UTF8)([^\w/\.\-\\% ])" {
add_header "response" $1;
return 200;
}
请求:
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /与 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
* Server nginx/1.4.6 (Ubuntu) is not blacklisted
< Server: nginx/1.4.6 (Ubuntu)
< Date: Tue, 20 Jan 2015 21:44:51 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
< Connection: keep-alive
< response: 与 <--- SEE THIS?
<
* Connection #0 to host localhost left intact
然而,当我删除UTF8标记时,标题包含“?”好像nginx无法理解字符(或只是读取第一个字节)。
location ~* "([^\w/\.\-\\% ])" {
add_header "response" $1;
return 200;
}
请求:
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /与 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
* Server nginx/1.4.6 (Ubuntu) is not blacklisted
< Server: nginx/1.4.6 (Ubuntu)
< Date: Tue, 20 Jan 2015 21:45:35 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
< Connection: keep-alive
< response: ?
<
* Connection #0 to host localhost left intact
注意:更改此非utf-8正则表达式以捕获一个或多个 ([^...]+)
也会导致发送response: 与
标头(字节与多字节字符串? )
将正则表达式匹配记录到文件会导致请求条目如下:
GET /\xE4\xB8\x8E HTTP/1.1
答案 0 :(得分:13)
除了正则表达式和终端配置之外,这与Unicode没有任何关系。对你的问题的简短回答是:nginx并不关心Unicode编码,但它确实接受了URL中的非ASCII字节。
以下是解释您所看到的内容的长答案。如果输入命令
curl http://localhost:8080/与
并且您的终端使用UTF-8作为编码,它将字符与(U + 4E0E)编码为三字节UTF-8序列
0xE4 0xB8 0x8E
curl
显然接受URL中的非ASCII字节,尽管它们在技术上是非法的。然后它将发送带有这些非ASCII字节的HTTP请求。由于没有默认的方式来显示这些字节,因此从现在开始我将使用粗体C样式的十六进制转义符,如 \ x00 来表示它们。因此curl
发送的请求行如下所示:
GET / \ xE4 \ xB8 \ x8E HTTP / 1.1
第一个/
之后的三个字节。如果您查看日志的终端也支持UTF-8,则会在屏幕上显示为
GET /与HTTP / 1.1
但是不意味着您的HTTP请求中有Unicode字符。在HTTP级别,我们只处理字节。
nginx似乎也很乐意接受URL中的非ASCII字节。然后是以下正则表达式
(*UTF8)([^\w/\.\-\\% ])
在UTF-8模式下工作将字节序列 \ xE4 \ xB8 \ x8E 视为与\w
匹配的字符,因此标题将为
回复: \ xE4 \ xB8 \ x8E
您的终端显示为
回复:与
另一方面,正则表达式
([^\w/\.\-\\% ])
直接在字节上工作,因此它只匹配路径的第一个字节,或者根本不匹配。出于某种原因,它认为序列 \ xE4 \ xB8 \ x8E 的第一个字节与\w
匹配(可能是因为它假定为Latin1或Windows-1252字符串),因此标题将是:
回复: \ xE4
您的终端决定显示为
回复:?
因为字节 \ xE4 后跟换行符是无效的UTF-8。正则表达式([^\w/\.\-\\% ])+
匹配整个字节序列,因此它产生的结果与UTF-8正则表达式相同。
如果您看到类似
的内容GET /\xE4\xB8\x8E HTTP/1.1
在您的日志中,因为日志代码的作者决定将转义序列用于非ASCII字节。一般来说,这是一个好主意,因为无论终端配置如何,它总是产生相同的输出,并且真正显示正在进行的操作:您的HTTP请求只包含非ASCII字节。
答案 1 :(得分:3)
您自己的测试似乎没有回答您的问题吗?
是的,nginx确实支持路径中的Unicode。
作为讨论的一点,nginx将在位置匹配之前规范化URL,如http://nginx.org/r/location中的文档所述。这就是为什么不同的“奇怪”请求(例如那些包含../
;或那些encoding ?
as %3F
, thus making it part of the filename, instead of signifying the parameters known as $args
)的请求最终可能会被一个看起来不像是一对一匹配的单个位置所服务肉眼。
此规范化也可以解释为什么“{1}}(预标准化)与access_log
(标准化)中”相同“字符串的显示方式不同。