我托管了一个 URL 中包含“”的网页,与托管静态文件project²
的磁盘目录相匹配。project²
基于 java 的客户端使用此页面从 URL(生物信息学软件IGV)加载数据。我的页面以http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
. 在浏览器中单击这些链接将导致 IGV 客户端(在 localhost 上运行)向GET http://example.org/project²/some/data/file.bam
我的服务器请求。
✅ Linux/Mac 上的 IGV 通过将此 URL 请求为 UTF-8 编码²
=来响应%C2%B2
,一切正常。
❌我新获得的Win-10用户的客户端请求²
= %B2
(windows-1252编码),导致404-not-found。
在尝试了几十件事之后,我对如何帮助这个用户束手无策。
我的印象是我应该能够在服务器端动态重写错误编码的 URL,以便它们最终仍然提供所需的数据,但我不知道使规则模式匹配的神奇字符组合转义字符。
我已经尝试过的事情
- 仔细检查 404 不是网络问题;我看到
GET %B2
我ssl_access_log
的 with404
作为返回的状态码,所以它确实是服务器在做它。 - “正确”方式:在将 URL 提供给客户端之前对 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
- 似乎编码信息没有从 webbrowser API-link-click 传输到 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
RewriteRule和RewriteRuleFlags文档也不能帮助我理解我应该如何编码Pattern
-part 以便它可以工作:-(
类似的问题在这里
- Apache .htaccess 能否将编码 URI 中的百分比编码从 Win-1252 转换为 UTF-8?-> 外部编码程序
rewritemap
似乎有点矫枉过正,因为它实际上只有一个文件夹project²
,所以我的范围更小。 - 在 NGinX 中将ASCII 百分比编码的位置重写为其 UTF-8 编码的等效相同问题,指向上述 Apache 问题。
您不能仅使用 mod_rewrite “转换编码”,但是,您可以在请求的 URL 中搜索特定的字符序列并“更正它”。
请注意,它
project²
作为查询字符串的一部分出现在您发布的示例 URL 中,但是,RewriteRule
模式(您在上面使用的)仅与 %-decoded URL 路径匹配(不包括查询字符串)。要匹配查询字符串,您需要使用附加RewriteCond
指令并匹配QUERY_STRING
(或THE_REQUEST
)服务器变量。请注意,
QUERY_STRING
(andTHE_REQUEST
) 服务器变量是 % 编码的(或者更确切地说,是从客户端发送的) - 它们没有经过 % 解码。请尝试以下操作:
反向引用
%1
和替换%2
字符串中的引用前面的CondPattern - 麻烦部分之前和之后的部分。/project%B2/
$1
只是对 URL 路径的反向引用(以保存重复),我假设它总是load
.该
NE
标志防止%
自身(当用作 URL 编码字符的一部分时)被 URL 编码。如果您需要匹配% 编码的 URL 路径,那么您应该匹配
THE-REQUEST
服务器变量。THE_REQUEST
包含 HTTP 请求标头的第一行并且未进行 % 解码。它包含从客户端发送的完整 URL 路径(和查询字符串)(以及请求方法和协议版本)。例如,对于格式错误的请求,格式如下的字符串:您可以匹配和更正如下:
%1
并且%2
是对前面CondPattern中捕获的子模式的反向引用。另一方面,
RewriteRule
pattern仅与预处理的 %-decoded URL-path 匹配(如上所述)。那么,%B2
无论解码为什么;假设是 UTF-8 编码。不幸的是,这是一个不可打印的字符,因此需要用正则表达式中的十六进制字符序列表示,即。\xb2
(这是表示单个字节序列的 PCRE 语法)。解决方案
RewriteRule
s 必须使用\x
而不是%
为了匹配 % 编码的 URL!(字节序列的 PCRE 语法)mod_rewrite
-config 使用 PCRE 正则表达式语法,并对解码的 URL 进行操作,因此%
在模式中键入 -encodingRewriteRule
会导致它查找文字%
-character,而不是编码值。RewriteRules 中正确的转义字符是,因此可以使用(或不区分大小写)匹配
\x
URLencoded 值。%B2
\xb2
\xB2
请注意,这
RewriteRule
是一种针对字符编码问题的 hacky 解决方案,它仅在特定的、可预测的位置恰好有一个特定的错误编码字符时才有效。有关任意位置多个错误编码字符的通用解决方案,请参阅Apache .htaccess 能否将编码 URI 中的百分比编码从 Win-1252 转换为 UTF-8?,它提出了一种通用解决方案,该解决方案使用
RewriteMap
功能齐全的编程语言耦合到外部程序。正确的解决方案仍然是从源头上防止这种情况,在整个链中使用显式的 %-encoding。这可以避免依赖于操作系统的编码意外发生在您无法控制的“中间某处”。(假设路径上没有客户端进行双重编码,这应该是一种应受惩罚的罪行..)
我是怎么到这里的
绝望了,我按照mod_rewrite docs
LogLevel Warn rewrite:trace3
中的建议提高了服务器范围的日志记录。警告这会(严重)影响服务器性能,但可以管理,因为这是一个低流量的服务器,并且没有预先存在的重写。额外的日志记录被发送到 (
ssl_
)error_log
中。这让我深入了解了匹配是如何尝试的,以及规则和 URI 的内部表示形式是什么mod_rewrite
。摘自
ssl_error_log
(为简洁起见省略了许多列),带有规则RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE,L]
请注意,来自客户端的 request-uri 是写的
\xb2
,但我的模式使用%B2
.使用规则将规则语法与 uri 语法匹配
RewriteRule (.*)project\xB2/(.*) $1project²/$2 [NE,L]
?成功!? 如我们所见,我们现在正在匹配!
为什么没有
[R]
/[R=302]
标志?由于这是一个字符编码问题,我不认为进行额外的 HTTP 往返会增加价值;馈入客户端的每个链接都会再次遇到相同的问题,除非我在将其馈入客户端 java 程序之前修复了编码问题。
不要忘记
RewriteBase
请注意,这个缩短的版本省略了正确的设置
RewriteBase
,这可能会破坏重写的路径,具体取决于您conf
写入的位置(例如<Directory>
vs<Location>
)。没有RewriteBase
我不小心重定向到❌https://example.org/var/www/html/rewrite-testing/project²
而不是✅https://example.org/rewrite-testing/project²
)