Nginx 是否支持路径中的原始 unicode?
Does Nginx support raw unicode in paths?
浏览器 url 默认将 unicode 字符编码为 %##。
但是,我可以通过 CURL 向 http://localhost:8080/与
发出请求,nginx 将路径视为“与
”。这怎么可能?那么 Nginx 是否允许在其路径中使用任意 unicode?
例如,使用这个配置我可以设置一个额外的 header 来查看 nginx 看到了什么:
location ~* "(*UTF8)([^\w/\.\-\% ])" {
add_header "response" ;
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 标记时,header 包含“?”好像 nginx 无法理解字符(或者只读取第一个字节)。
location ~* "([^\w/\.\-\% ])" {
add_header "response" ;
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
注意:更改此 non-utf-8 正则表达式以捕获 one-or-more ([^...]+)
也会导致 response: 与
header 正在发送(字节与多字节字符串?)
将正则表达式匹配记录到文件会导致请求条目如下:
GET /\xE4\xB8\x8E HTTP/1.1
除了正则表达式和终端配置,这与 Unicode 没有任何关系。对您的问题的简短回答是:nginx 不关心 Unicode 编码,但它确实接受 URL 中的 non-ASCII 字节。
这是解释您所见内容的长答案。如果输入命令
curl http://localhost:8080/与
并且您的终端使用 UTF-8 作为编码,它将字符与 (U+4E0E) 编码为 three-byte UTF-8 序列
0xE4 0xB8 0x8E
curl
显然接受 URL 中的 non-ASCII 字节,尽管它们在技术上是非法的。然后它将发送一个带有这些 non-ASCII 字节的 HTTP 请求。由于没有默认方式来显示这些字节,因此从现在开始我将使用粗体 C-style 十六进制转义符 \x00 来表示它们。所以 curl
发送的请求行看起来像:
GET /\xE4\xB8\x8E HTTP/1.1
这是第一个 /
之后的三个字节。如果您查看日志的终端也支持 UTF-8,这将在您的屏幕上显示为
GET /与 HTTP/1.1
但这并不意味着您的 HTTP 请求中有 Unicode 字符。在 HTTP 级别,我们只处理字节。
nginx 似乎也乐于接受 URL 中的 non-ASCII 字节。然后是下面的正则表达式
(*UTF8)([^\w/\.\-\% ])
在 UTF-8 模式下工作将字节序列 \xE4\xB8\x8E 视为匹配 \w
的字符与,因此 header 将是
response: \xE4\xB8\x8E
您的终端显示为
response: 与
另一方面,正则表达式
([^\w/\.\-\% ])
直接在字节上工作,所以它只会匹配你路径的第一个字节,或者根本不匹配。出于某种原因,它认为序列 \xE4\xB8\x8E 的第一个字节匹配 \w
(可能是因为它假设了 Latin1 或 Windows-1252 字符串) ,所以 header 将是:
response: \xE4
您的终端决定显示为
response: ?
因为字节 \xE4 后跟一个换行符是无效的 UTF-8。正则表达式 ([^\w/\.\-\% ])+
匹配整个字节序列,因此它产生与 UTF-8 正则表达式相同的结果。
如果您看到类似
的内容
GET /\xE4\xB8\x8E HTTP/1.1
在您的日志中,那是因为日志记录代码的作者决定对 non-ASCII 字节使用转义序列。一般来说,这是一个好主意,因为无论终端配置如何,它总是产生相同的输出,并且真正显示了正在发生的事情:您的 HTTP 请求仅包含 non-ASCII 字节。
您自己的测试似乎已经回答了您的问题?
是的,nginx 支持路径中的 Unicode。
作为讨论的要点,nginx 将在位置匹配之前规范化 URL,正如 http://nginx.org/r/location. Which is why different "weird" requests (like those containing ../
; or those encoding ?
as %3F
, thus making it part of the filename, instead of signifying the parameters known as $args
的文档中所指出的那样)可能最终仍由看起来不像一个位置的单个位置提供服务肉眼一比。
这种标准化也可以解释为什么 "same" 字符串在 access_log
(预标准化)和 error_log
(标准化)中出现不同。
浏览器 url 默认将 unicode 字符编码为 %##。
但是,我可以通过 CURL 向 http://localhost:8080/与
发出请求,nginx 将路径视为“与
”。这怎么可能?那么 Nginx 是否允许在其路径中使用任意 unicode?
例如,使用这个配置我可以设置一个额外的 header 来查看 nginx 看到了什么:
location ~* "(*UTF8)([^\w/\.\-\% ])" {
add_header "response" ;
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 标记时,header 包含“?”好像 nginx 无法理解字符(或者只读取第一个字节)。
location ~* "([^\w/\.\-\% ])" {
add_header "response" ;
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
注意:更改此 non-utf-8 正则表达式以捕获 one-or-more ([^...]+)
也会导致 response: 与
header 正在发送(字节与多字节字符串?)
将正则表达式匹配记录到文件会导致请求条目如下:
GET /\xE4\xB8\x8E HTTP/1.1
除了正则表达式和终端配置,这与 Unicode 没有任何关系。对您的问题的简短回答是:nginx 不关心 Unicode 编码,但它确实接受 URL 中的 non-ASCII 字节。
这是解释您所见内容的长答案。如果输入命令
curl http://localhost:8080/与
并且您的终端使用 UTF-8 作为编码,它将字符与 (U+4E0E) 编码为 three-byte UTF-8 序列
0xE4 0xB8 0x8E
curl
显然接受 URL 中的 non-ASCII 字节,尽管它们在技术上是非法的。然后它将发送一个带有这些 non-ASCII 字节的 HTTP 请求。由于没有默认方式来显示这些字节,因此从现在开始我将使用粗体 C-style 十六进制转义符 \x00 来表示它们。所以 curl
发送的请求行看起来像:
GET /\xE4\xB8\x8E HTTP/1.1
这是第一个 /
之后的三个字节。如果您查看日志的终端也支持 UTF-8,这将在您的屏幕上显示为
GET /与 HTTP/1.1
但这并不意味着您的 HTTP 请求中有 Unicode 字符。在 HTTP 级别,我们只处理字节。
nginx 似乎也乐于接受 URL 中的 non-ASCII 字节。然后是下面的正则表达式
(*UTF8)([^\w/\.\-\% ])
在 UTF-8 模式下工作将字节序列 \xE4\xB8\x8E 视为匹配 \w
的字符与,因此 header 将是
response: \xE4\xB8\x8E
您的终端显示为
response: 与
另一方面,正则表达式
([^\w/\.\-\% ])
直接在字节上工作,所以它只会匹配你路径的第一个字节,或者根本不匹配。出于某种原因,它认为序列 \xE4\xB8\x8E 的第一个字节匹配 \w
(可能是因为它假设了 Latin1 或 Windows-1252 字符串) ,所以 header 将是:
response: \xE4
您的终端决定显示为
response: ?
因为字节 \xE4 后跟一个换行符是无效的 UTF-8。正则表达式 ([^\w/\.\-\% ])+
匹配整个字节序列,因此它产生与 UTF-8 正则表达式相同的结果。
如果您看到类似
的内容GET /\xE4\xB8\x8E HTTP/1.1
在您的日志中,那是因为日志记录代码的作者决定对 non-ASCII 字节使用转义序列。一般来说,这是一个好主意,因为无论终端配置如何,它总是产生相同的输出,并且真正显示了正在发生的事情:您的 HTTP 请求仅包含 non-ASCII 字节。
您自己的测试似乎已经回答了您的问题?
是的,nginx 支持路径中的 Unicode。
作为讨论的要点,nginx 将在位置匹配之前规范化 URL,正如 http://nginx.org/r/location. Which is why different "weird" requests (like those containing ../
; or those encoding ?
as %3F
, thus making it part of the filename, instead of signifying the parameters known as $args
的文档中所指出的那样)可能最终仍由看起来不像一个位置的单个位置提供服务肉眼一比。
这种标准化也可以解释为什么 "same" 字符串在 access_log
(预标准化)和 error_log
(标准化)中出现不同。