3

我们有一个带有动态 URL 方案的 PHP 应用程序,它需要对字符进行百分比编码,甚至是“非保留字符”,例如实际上不需要编码的括号或撇号。应用程序认为以“错误”方式编码的 URL 被规范化,然后重定向到“正确”编码。

但是谷歌和其他用户代理将以不同的方式规范百分比编码/解码,这意味着当 Googlebot 请求页面时,它会要求“错误”的 URL,当它返回到“正确”URL 的重定向时,Googlebot 将拒绝跟随重定向并将拒绝索引该页面。

是的,这是我们这边的一个错误。HTTP 规范要求服务器同等对待百分比编码和非百分比编码的未保留字符。但是现在解决应用程序代码中的问题并不简单,所以我希望通过使用 Apache 重写规则来避免代码更改,这将确保从应用程序的角度“正确”编码 URL , 这意味着撇号、括号等都是百分比编码的,并且空格被编码为+而不是%20

这是一个例子,我想重写第一种形式并最终得到第二种形式:

  • www.splunkbase.com/apps/All/4.x/Add-On/app:OPSEC+LEA+for+Check+Point+(Linux)
  • www.splunkbase.com/apps/All/4.x/Add-On/app:OPSEC+LEA+for+Check+Point+%28Linux%29

这是另一个:

  • www.splunkbase.com/apps/All/4.x/app:Benford's+Law+Fraud+Detection+Add-on
  • www.splunkbase.com/apps/All/4.x/app:Benford%27s+Law+Fraud+Detection+Add-on

这是另一个:

  • www.splunkbase.com/apps/All/4.x/app:Benford%27s%20Law%20Fraud%20Detection%20Add-on
  • www.splunkbase.com/apps/All/4.x/app:Benford%27s+Law+Fraud+Detection+Add-on

如果应用只看到这些 URL 的第二种形式,则它不会发送任何重定向,Google 将能够为该页面编制索引。

我是一个有重写规则的新手,从我对mod-rewrite 文档的阅读中很明显,mod_rewrite 做了一些自动编码/解码,这可能有助于或伤害我想做的事情,尽管不确定。

关于重写规则以处理上述情况的任何建议?我可以为每个特殊字符设置一个规则,因为它们并不多,但是一个规则(如果可能的话)将是理想的。

4

2 回答 2

2

该解决方案实际上可能相当简单,尽管由于使用了Bflag ,它只能在 Apache 2.2 及更高版本中工作。我不确定它是否正确处理了每个案例(诚然我有点怀疑它不涉及比这更多的工作),但我被引导相信它应该由源代码。

还要记住,REQUEST_URImod_rewrite 转换不会更新 的值,因此如果您的应用程序依赖该值来确定请求的 URL,那么您所做的更改无论如何都不会可见。

好消息是这可以在 .htaccess 中完成,因此如果对您更有效,您可以选择保持主配置不变。

RewriteEngine On

# Make sure this is only done once to avoid escaping the escapes...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# Check if we have anything to bother escaping (likely unnecessary...)
RewriteCond $0 [^\w]+
# Rewrite the entire URL by escaping the backreference
RewriteRule ^.*$ $0 [B]

那么,为什么需要使用B标志而不是让 mod_rewrite 自动转义重写的 URL?当 mod_rewrite 自动转义 URL 时,它使用ap_escape_uri(显然由于某种原因已变成宏ap_os_escape_path......),一个转义有限字符子集的函数。B然而,该标志使用一个名为 的内部模块函数escape_uri,该函数以 PHP 的urlencode函数为模型。

模块中的实现escape_uri建议字母数字字符和下划线保持原样,空格转换为+,其他所有内容都转换为其转义等效项。这似乎是您想要的行为,所以它应该可以工作。

如果没有,您可以选择设置一个外部程序,该程序RewriteMap可以将传入的 URL 操作为正确的格式。不过,这需要操作 Apache 配置,并且叛徒脚本可能会导致整个服务器出现问题,因此如果可以避免,我认为它不是一个理想的解决方案。

于 2010-09-28T00:30:33.017 回答
1

mod_rewrite 不是完成此类工作的最佳工具。因为使用 mod_rewrite 您一次只能替换固定数量的事件。但有可能:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?\ ]*)%20([^?\ ]*)
RewriteRule ^ /%1+%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*)
RewriteRule ^ /%1\%27%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*)
RewriteRule ^ /%1\%28%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*)
RewriteRule ^ /%1\%29%2 [R=301,NE]

这将一次替换一个%20'(),并以 301 重定向响应。因此,如果 URL 路径包含 10 个需要替换的字符,则需要 10 次重定向才能这样做。

由于这可能不是最佳解决方案,因此可以使用N标志进行除最后一个内部替换之外的所有替换,并且仅使用重定向在外部进行最后一个替换:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(([^?%\ ]|%(2[1-9a-fA-F]|[013-9][0-9a-fA-F]))*)%20(([^?%\ ]|%(2[1-9a-fA-F]|[013-9][0-9a-fA-F]))*%20[^?\ ]*)
RewriteRule ^ /%1+%4 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?\ ]*)%20([^?\ ]*)[?\ ]
RewriteRule ^ /%1+%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*'[^?\ ]*)
RewriteRule ^ /%1\%27%2 [N,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*)[?\ ]
RewriteRule ^ /%1\%27%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*\([^?\ ]*)
RewriteRule ^ /%1\%28%2 [N,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*)[?\ ]
RewriteRule ^ /%1\%28%2 [R=301,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*\)[^?\ ]*)
RewriteRule ^ /%1\%29%2 [N,NE]
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*)[?\ ]
RewriteRule ^ /%1\%29%2 [R=301,NE]

但是使用N标志可能很危险,因为它不会增加内部递归计数器,因此很容易导致无限递归。

于 2010-09-27T18:07:25.853 回答