Nginx是否支持路径中的原始unicode?

Xeo*_*oss 7 unicode url nginx

浏览器url默认将unicode字符编码为%##.

但是,我可以通过CURL发出请求,http://localhost:8080/?而nginx将路径视为" ?".这怎么可能?Nginx是否允许在其路径中使用任意unicode?

例如,使用此配置,我可以设置一个额外的标头,以查看nginx看到了什么:

location ~* "(*UTF8)([^\w/\.\-\\% ])" {
        add_header "response" $1;
        return 200;
}
Run Code Online (Sandbox Code Playgroud)

请求:

* 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
Run Code Online (Sandbox Code Playgroud)

但是,当我删除UTF8标记时,标题包含"?" 好像nginx无法理解字符(或只是读取第一个字节).

location ~* "([^\w/\.\-\\% ])" {
        add_header "response" $1;
        return 200;
}
Run Code Online (Sandbox Code Playgroud)

请求:

* 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
Run Code Online (Sandbox Code Playgroud)

注意:更改此非utf-8正则表达式以捕获一个或多个 ([^...]+)也导致response: ?标头被发送(字节与多字节字符串?)

将正则表达式匹配记录到文件会导致请求条目如下:

GET /\xE4\xB8\x8E HTTP/1.1
Run Code Online (Sandbox Code Playgroud)

nwe*_*hof 13

除了正则表达式和终端配置之外,这与Unicode没有任何关系.对你的问题的简短回答是:nginx不关心Unicode编码,但它接受URL中的非ASCII字节.

这是解释您所看到的内容的长答案.如果输入命令

curl http://localhost:8080/?
Run Code Online (Sandbox Code Playgroud)

并且您的终端使用UTF-8作为编码,它将字符与(U + 4E0E)编码为三字节UTF-8序列

0xE4 0xB8 0x8E
Run Code Online (Sandbox Code Playgroud)

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/\.\-\\% ])
Run Code Online (Sandbox Code Playgroud)

在UTF-8模式下工作将字节序列\ xE4\xB8\x8E视为匹配的字符\w,因此标题将是

响应:\ xE4\xB8\x8E

你的终端显示为

回应:与

另一方面,正则表达式

([^\w/\.\-\\% ])
Run Code Online (Sandbox Code Playgroud)

直接在字节上工作,因此它只匹配路径的第一个字节,或者根本不匹配.由于某种原因,它认为序列的第一个字节\ xE4\xB8\x8E匹配\w(可能是因为它假定为Latin1或Windows-1252字符串),因此标题将为:

回复:\ xE4

您的终端决定显示为

回应:?

因为字节\ xE4后跟换行符是无效的UTF-8.正则表达式([^\w/\.\-\\% ])+匹配整个字节序列,因此它产生与UTF-8正则表达式相同的结果.

如果你看到类似的东西

GET /\xE4\xB8\x8E HTTP/1.1
Run Code Online (Sandbox Code Playgroud)

在您的日志中,这是因为日志代码的作者决定将转义序列用于非ASCII字节.一般来说,这是一个好主意,因为无论终端配置如何,它总是产生相同的输出,并且真正显示正在发生的事情:您的HTTP请求只包含非ASCII字节.