我们有一个带有动态URL方案的PHP应用程序,它要求字符进行百分比编码,甚至“unreserved characters”,如括号或aphostrophes,实际上不需要编码。应用程序认为以“错误”方式编码的URL被规范化,然后重定向到“正确”编码。
但Google和其他用户代理会规范化百分比编码/解码,这意味着当Googlebot请求该网页时,它会询问“错误”的网址,当它返回重定向到“正确”的网址时,Googlebot会拒绝遵循重定向,并拒绝索引页面。
是的,这是我们最终的错误。 HTTP规范要求服务器以相同方式处理百分比编码和非百分比编码的非保留字符。但是现在修复应用程序代码中的问题是非常简单的,所以我希望通过使用Apache重写规则来避免代码更改,这将确保从应用程序的角度“正确”编码URL ,意味着apopstrophes,括号等都是百分比编码的,并且空格编码为+
而不是%20
。
这是一个例子,我想重写第一个表格并以第二个表格结束:
这是另一个:
这是另一个:
如果应用只看到这些网址的第二种形式,那么它就不会发送任何重定向,Google也可以为该网页编制索引。
我是一个有重写规则的新手,从我对mod-rewrite documentation的阅读中可以清楚地看出,mod_rewrite会做一些自动编码/解码,这可能有助于或伤害我想做的事情,虽然不确定。
有关处理上述案件的重写规则的建议吗?我对每个特殊角色都有一个规则,因为它们不是很多,但是一个规则(如果可能的话)将是理想的。
答案 0 :(得分:2)
解决方案实际上可能相当简单,但由于使用了B
flag,它只能在Apache 2.2及更高版本中使用。我不确定它是否正确地处理了每一个案例(诚然,我有点怀疑它不涉及比这更多的工作),但我被引导相信它应该由源代码。
请记住,REQUEST_URI
的值不会被mod_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
的实现表明,字母数字字符和下划线保持原样,空格转换为+,其他所有内容都转换为其转义等效字符。这似乎是你想要的行为,所以可能它应该有效。
如果没有,您可以选择设置可以将传入的URL操作为正确格式的外部程序RewriteMap
。这需要操纵Apache配置,并且叛逆脚本可能会导致整个服务器出现问题,因此如果可以避免,我认为它不是理想的解决方案。
答案 1 :(得分: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 flag进行除最后一次内部替换之外的所有替换,并且只能使用重定向从外部替换最后一次:
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 标志可能很危险,因为它不会增加内部递归计数器,因此很容易导致无限递归。