我托管一个网页,其 URL 中包含“ ”,与托管静态文件的project²
磁盘目录匹配。project²
该页面由基于 Java 的客户端用于从 URL 加载数据(生物信息学软件进气歧管)。我的页面以 的形式列出 URL http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
。在浏览器中单击这些链接将导致 IGV 客户端(在本地主机上运行)GET http://example.org/project²/some/data/file.bam
从我的服务器发出请求。
✅ Linux/Mac 上的 IGV 通过以 UTF-8 编码请求此 URL 来响应²
= %C2%B2
,并且一切正常运行。❌
我新获得的 Win-10 用户的客户端请求²
= %B2
(windows-1252 编码),导致 404 未找到。
尝试了几十种方法后,我还是不知道该如何帮助这位用户。
我认为我应该能够在服务器端动态重写错误编码的 URL,以便它们最终仍能提供所需的数据,但是我不知道使规则模式与转义字符匹配的神奇字符组合。
我已经尝试过的事情
- 再次检查 404 不是网络问题;我看到 with中
GET %B2
出现了返回的状态代码,所以这确实是服务器造成的。ssl_access_log
404
- '正确'的方式:在将 URL 提供给客户端之前对其进行 UrlEncoding。Perl
URI::Encode
encode_uri
会将其转换²
为%C3%82%C2%B2
(显然ò
?),即更加错误不知何故? - 再次检查提供加载 URL 的网页是否为 utf-8
- 它提供标题
Content-Type: text/html; charset=UTF-8
- 设置
AddDefaultCharset UTF-8
httpd.conf
- 似乎编码信息没有通过 Web 浏览器 API-链接-点击传输到 Java 程序中
- 它提供标题
- 通过符号链接将目录加倍
,并且projectª -> project²
project%B2 -> project²
(编辑:ª 没有任何关系;不知道我从哪里得到这个ª
是 UTF8 匹配%B2
) - 尝试
mod_rewrite
用几种不同的方法将“坏” URL 转换为好 URL,但似乎都不起作用:
RewriteEngine on
# RewriteRule Pattern Substitution [flags]
RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE] # encoded 'bad' request, unencoded redirect
RewriteRule (.*)²(.*) $1%C2%B2$2 [B,NE] # config file is utf-8 encoded, so this is senseless.
RewriteRule (.*)%B2(.*) $12$2 [B,NE] # doesn't match?
RewriteRule (.*)TZZT(.*) $1test$2 # works, so RewriteEngine is working
这重写规则和重写规则标志文档也没有帮助我理解如何对 -part 进行编码Pattern
以使其工作:-(
这里有类似的问题
- Apache .htaccess 可以将编码 URI 中的百分比编码从 Win-1252 转换为 UTF-8 吗?- > 外部编码程序
rewritemap
似乎有点过度,因为它实际上只有一个文件夹project²
,所以我的范围较小。 - 将 ASCII 百分比编码的位置重写为其 UTF-8 编码的等效位置NGinX 中存在同样的问题,指向上述 Apache 问题。
答案1
您不能仅使用 mod_rewrite 来“转换编码”,但是,您可以在请求的 URL 中搜索特定的字符序列并“更正它”。
http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE]
注意project²
作为请求参数但是,在您发布的示例 URL 中,RewriteRule
图案(上面使用的)仅与 % 解码的 URL 路径匹配(不包括查询字符串)。要与查询字符串匹配,您需要使用附加指令并与(或)服务器变量RewriteCond
匹配。QUERY_STRING
THE_REQUEST
请注意,QUERY_STRING
(和THE_REQUEST
)服务器变量是%编码的(或者更确切地说,从客户端发送的) - 它们尚未经过%解码。
请尝试以下操作:
RewriteCond %{QUERY_STRING} (.+)/project%B2/(.*)
RewriteRule ^(load)$ $1?%1/project%C2%B2/%2 [NE,L]
反向引用%1
和%2
引用代换字符串引用前面的条件模式- 麻烦部分之前和之后的部分/project%B2/
。
$1
只是对 URL 路径的反向引用(为了避免重复),我假设它始终是load
。
该NE
标志可防止其%
本身(当用作 URL 编码字符的一部分时)被进行 URL 编码。
更新:恐怕我最初的问题不清楚谁获取了哪个 URL,因此您答案中的“查询字符串”部分不适用......
如果你需要匹配%-编码的 URL 路径那么您应该与THE-REQUEST
服务器变量进行匹配。THE_REQUEST
包含 HTTP 请求标头的第一行,并且未经过 % 解码。它包含从客户端发送的完整 URL 路径(和查询字符串)(以及请求方法和协议版本)。例如,在格式错误的请求的情况下,格式为以下字符串:
GET /project%B2/some/data/file.bam HTTP/1.1
您可以按如下方式匹配和更正:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,7}\s(/project)%B2([^\s]+)
RewriteRule ^/?project %1%B2%C2%2 [NE,L]
%1
并且%2
反向引用前面捕获的子模式条件模式。
这RewriteRule
图案另一方面,仅与预处理的 % 解码 URL 路径匹配(如上所述)。因此,%B2
是解码后的任何内容;假设采用 UTF-8 编码。不幸的是,这是一个不可打印的字符,因此需要用正则表达式中的十六进制字符序列表示,即。\xb2
(这是表示单个字节序列的 PCRE 语法)。
答案2
解决方案
RewriteRule
必须使用 s\x
而不是%
来匹配 % 编码的 URL!(字节序列的 PCRE 语法)
mod_rewrite
-config 使用 PCRE 正则表达式语法,并对解码的 URL 进行操作,因此%
在RewriteRule
模式中输入 -encoding 会导致它查找文字%
-character,而不是编码值。RewriteRules
中的正确转义字符是,因此可以使用(或,不区分大小写)匹配\x
URLencoded 值。%B2
\xb2
\xB2
请注意,这RewriteRule
是解决字符编码问题的一种不成熟的解决方案,仅当在特定的、可预测的位置上只有一个特定的错误编码字符时才有效。
对于任意位置出现多个错误编码字符的通用解决方案,请参阅Apache .htaccess 可以将编码 URI 中的百分比编码从 Win-1252 转换为 UTF-8 吗?RewriteMap
,它提出了一种使用全功能编程语言与外部程序相结合的通用解决方案。
这恰当的解决方案仍然是从源头上防止这种情况,在整个链中使用显式 % 编码。这可以避免操作系统相关的编码意外发生在“中间某处”,超出您的控制范围。(假设路径上没有客户端进行双重编码,这应该是应受惩罚的罪行。)
我是如何来到这里的
LogLevel Warn rewrite:trace3
绝望之下,我按照建议提高了服务器范围的日志记录mod_rewrite 文档。警告称,这将严重影响服务器性能,但由于这是一个低流量服务器,且没有预先存在的重写,因此是可控的。
附加日志被发送到 ( ssl_
)中error_log
。这使我深入了解了匹配尝试的具体方式,以及规则和 URI 的内部表示mod_rewrite
。
摘录自ssl_error_log
(为简洁起见省略了许多列),附有规则RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE,L]
[rewrite:trace3] applying pattern '(.*)project%B2/(.*)' to uri 'project\xb2/'
[rewrite:trace1] pass through /var/www/html/example.org/project\xb2
请注意,来自客户端的请求 uri 写为\xb2
,但我的模式使用%B2
。
使用规则将规则语法与 uri 语法进行匹配RewriteRule (.*)project\xB2/(.*) $1project²/$2 [NE,L]
[rewrite:trace3] applying pattern '(.*)project\\xb2/(.*)' to uri 'project\xb2/'
[rewrite:trace2] rewrite 'project\xb2/' -> 'project%c2%b2/'
[rewrite:trace1] internal redirect with /auth-test/project\xc2\xb2/ [INTERNAL REDIRECT]