调试.htaccess重写规则的技巧

许多海报在他们的.htaccess文件中调试他们的RewriteRule和RewriteCond语句时会遇到问题。其中大多数使用共享托管服务,因此无法访问根服务器配置。他们不能避免使用.htaccess文件重写和不能启用RewriteLogLevel"正如许多受访者所言。此外,还有许多__abc0特有的陷阱和约束没有很好地覆盖。对大多数人来说,建立一个本地测试LAMP堆栈涉及太多的学习曲线。

所以我这里的问题是我们如何建议他们< >强调试< / >强他们的规则自己。以下是我的一些建议。其他建议将不胜感激。

  1. < p > mod_rewrite引擎循环使用.htaccess文件。引擎运行这个循环:

    do
    execute server and vhost rewrites (in the Apache Virtual Host Config)
    find the lowest "Per Dir" .htaccess file on the file path with rewrites enabled
    if found(.htaccess)
    execute .htaccess rewrites (in the user's directory)
    while rewrite occurred
    

    因此,你的规则将被重复执行,如果你改变URI路径,那么它可能最终执行其他__abc0file(如果它们存在)。因此,请确保您终止了此循环,如有必要,可以通过添加额外的RewriteCond来停止规则触发。也要删除任何较低级别的.htaccess重写规则集,除非明确地打算使用多级规则集。

  2. 确保每个Regexp的语法是正确的通过测试一组测试模式,以确保是一个有效的语法,并对所有测试uri进行测试。详见下面的回答

  3. 在测试目录中增量地构建规则。你可以使用“在路径特征上执行最深的.htaccess文件”;在这里设置一个单独的测试目录(树)和调试规则集,而不会破坏你的主要规则并停止你的网站工作。您必须一次添加一个,因为这是将失败本地化到单个规则的唯一方法。

  4. < p > 使用一个虚拟脚本存根来转储服务器和环境变量。(参见清单2)如果你的应用程序使用blog/index.php,那么你可以将其复制到test/blog/index.php中,并使用它来测试你在test子目录中的博客规则。你也可以使用环境变量来确保重写引擎正确地解释替换字符串。

    RewriteRule ^(.*) - [E=TEST0:%{DOCUMENT_ROOT}/blog/html_cache/$1.html]
    

    并在phpinfo转储中查找这些REDIRECT_ *变量。顺便说一句,我使用了这个,并在我的网站上发现我必须使用%{ENV:DOCUMENT_ROOT_REAL}来代替。在重定向器循环的情况下,REDIRECT_REDIRECT_ *变量列出了前一遍。等。

  5. < p > 确保你不会被浏览器缓存错误的301重定向所困扰。看到下面的回答。我要感谢乌尔里希Palha

  6. 重写引擎似乎对.htaccess上下文中的级联规则很敏感,(这就是RewriteRule导致替换的地方,而这将下降到进一步的规则),因为我发现了内部子请求(1)的错误,以及不正确的PATH_INFO处理,这通常可以通过使用[NS], [L]和[PT]标志来防止。

还有什么评论或建议吗?

清单1——phpinfo

<?php phpinfo(INFO_ENVIRONMENT|INFO_VARIABLES);
135983 次浏览

下面是一些关于测试规则的额外技巧,可以简化共享主机上用户的调试

1. 使用伪用户代理

当测试一个新规则时,添加一个条件,只使用你将用于请求的fake用户代理执行它。这样就不会影响到你网站上的其他人。

#protect with a fake user agent
RewriteCond %{HTTP_USER_AGENT}  ^my-fake-user-agent$
#Here is the actual rule I am testing
RewriteCond %{HTTP_HOST} !^www\.domain\.com$ [NC]
RewriteRule ^ http://www.domain.com%{REQUEST_URI} [L,R=302]

如果使用Firefox,可以使用用户代理切换器创建假用户代理字符串并进行测试。

2. 在测试完成之前不要使用301

我见过很多帖子,人们仍然在测试他们的规则,他们使用的是301。

如果你没有在你的网站上使用建议1,那么不仅是你,而且当时访问你网站的任何人都将受到301的影响。

记住它们是永久的,并且被你的浏览器积极缓存。 在你确定之前,先用302代替,然后再改成301

3.请记住,301缓存在您的浏览器中

如果您的规则不起作用,并且看起来对您来说是正确的,并且您没有使用建议1和2,那么在清除浏览器缓存后或在私人浏览时重新测试。

4. 使用HTTP捕获工具

使用像提琴手这样的HTTP捕获工具来查看浏览器和服务器之间的实际HTTP流量。

虽然其他人可能会说你的site does not look right,但你可以相反地看到并报告all of the images, css and js are returning 404 errors,迅速缩小问题范围。

虽然其他人会报告你started at URL A and ended at URL C,但你将能够看到他们从URL A, were 302 redirected to URL B and 301 redirected to URL C开始。即使URL C是最终目标,你也会知道这对SEO不利,需要修复。

您将能够看到在服务器端设置的缓存头,重放请求,修改请求头以测试....


确保每个Regexp的语法是正确的

通过对一组测试模式进行测试,以确保语法有效,并对所有测试uri执行您想要的操作。

下面的regexpCheck.php是一个简单的脚本,你可以把它添加到你网站的私有/test目录中来帮助你做到这一点。我说得很简单。只需将其放入测试目录中的regexpCheck.php文件中,即可在您的网站上使用它。这将帮助您构建任何regexp,并在此过程中根据测试用例列表对其进行测试。我在这里使用的是PHP PCRE引擎,但在查看了Apache源代码之后,这与Apache中使用的引擎基本相同。有许多HowTos和教程提供模板,可以帮助您构建regexp技能。

清单1——regexpCheck.php

<html><head><title>Regexp checker</title></head><body>
<?php
$a_pattern= isset($_POST['pattern']) ? $_POST['pattern'] : "";
$a_ntests = isset($_POST['ntests']) ? $_POST['ntests'] : 1;
$a_test   = isset($_POST['test']) ? $_POST['test'] : array();
    

$res = array(); $maxM=-1;
foreach($a_test as $t ){
$rtn = @preg_match('#'.$a_pattern.'#',$t,$m);
if($rtn == 1){
$maxM=max($maxM,count($m));
$res[]=array_merge( array('matched'),  $m );
} else {
$res[]=array(($rtn === FALSE ? 'invalid' : 'non-matched'));
}
}
?> <p>&nbsp; </p>
<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME'];?>">
<label for="pl">Regexp Pattern: </label>
<input id="p" name="pattern" size="50" value="<?php echo htmlentities($a_pattern,ENT_QUOTES,"UTF-8");;?>" />
<label for="n">&nbsp; &nbsp; Number of test vectors: </label>
<input id="n" name="ntests"  size="3" value="<?php echo $a_ntests;?>"/>
<input type="submit" name="go" value="OK"/><hr/><p>&nbsp;</p>
<table><thead><tr><td><b>Test Vector</b></td><td>&nbsp; &nbsp; <b>Result</b></td>
<?php
for ( $i=0; $i<$maxM; $i++ ) echo "<td>&nbsp; &nbsp; <b>\$$i</b></td>";
echo "</tr><tbody>\n";
for( $i=0; $i<$a_ntests; $i++ ){
echo '<tr><td>&nbsp;<input name="test[]" value="',
htmlentities($a_test[$i], ENT_QUOTES,"UTF-8"),'" /></td>';
foreach ($res[$i] as $v) { echo '<td>&nbsp; &nbsp; ',htmlentities($v, ENT_QUOTES,"UTF-8"),'&nbsp; &nbsp; </td>';}
echo "</tr>\n";
}
?> </table></form></body></html>

我在调试我的mod_rewrite问题时发现了这个问题,它肯定有一些有用的建议。但最后最重要的事情是确保你的正则表达式语法正确。由于我自己的RE语法存在问题,安装regexpCheck.php脚本不是一个可行的选择。

但是由于Apache使用与perl兼容的正则表达式(PCRE),任何有助于编写PCRE的工具都应该有帮助。我曾经在Java和Javascript REs中使用过RegexPlanet的工具,并且很高兴地发现它们也支持Perl。

只需输入正则表达式和一个或多个示例URL,它就会告诉你正则表达式是否匹配(“~=”列中的“1”),如果适用,任何匹配组(“split”列中的数字将对应于Apache期望的数字,例如$1,$2等)。他们声称PCRE支持是“测试版”,但这正是我解决语法问题所需要的。

http://www.regexplanet.com/advanced/perl/index.html

我本可以简单地在已有的答案上添加一条评论,但我的声誉还没有达到那个水平。希望这能帮助到一些人。

确保在变量前面使用百分号,而不是美元号。

它是%{HTTP_HOST} ${HTTP_HOST}。在error_log中什么也没有,也没有内部服务器错误,您的regexp仍然是正确的,规则只是不匹配。如果你经常使用django / genshi模板,并且在肌肉记忆中使用${}进行变量替换,这真的很可怕。

一个是我浪费的几个小时:

如果你已经应用了所有这些技巧,但因为你没有访问服务器错误日志而只出现了500个错误,也许问题不在.htaccess文件中,而在它重定向到的文件中。

在我修复了我的.htaccess问题之后,我又花了两个多小时试图修复它,即使我只是忘记了一些权限。

关于4。,您仍然需要确保您的“虚拟脚本存根”在所有重写完成后实际上是目标URL,否则您将看不到任何东西!

类似的/相关的技巧(参见这个问题)是插入一个临时规则,例如:

RewriteRule (.*) /show.php?url=$1 [END]

其中show.php是一个非常简单的脚本,只显示它的$_GET参数(如果你愿意,你也可以显示环境变量)。

这将在您将其插入规则集时停止重写,就像调试器中的断点一样。

如果你正在使用Apache <2.3.9,你将需要使用[L]而不是[END],然后你五月需要添加:

RewriteRule ^show.php$ - [L]

在规则集的最顶端,如果 URL /show.php本身正在被重写。

不要忘记,在.htaccess文件中,它是一个相对URL匹配。

在.htaccess文件中,下面的RewriteRule永远不会匹配:

RewriteRule ^/(.*)     /something/$s

(类似于Doin的想法) 为了显示正在匹配的内容,我使用以下代码

$keys = array_keys($_GET);
foreach($keys as $i=>$key){
echo "$i => $key <br>";
}
将其保存到服务器根目录下的r.php,然后在.htaccess中进行一些测试 例如,我想匹配不以语言前缀

开始的url
RewriteRule ^(?!(en|de)/)(.*)$ /r.php?$1&$2 [L] #$1&$2&...
RewriteRule ^(.*)$ /r.php?nomatch [L] #report nomatch and exit

在线。htaccess重写测试

我发现谷歌RegEx帮助,它为我节省了很多时间,每次我做一个小的修改都要上传新的.htaccess文件。

来自网站:

htaccess测试仪

要测试你的htaccess重写规则,只需填写你正在应用规则的url,将你的htaccess的内容放在更大的输入区域,然后按“现在检查”。按钮。

设置环境变量并使用报头接收它们:

你可以用RewriteRule行创建新的环境变量,如OP所述:

RewriteRule ^(.*) - [E=TEST0:%{DOCUMENT_ROOT}/blog/html_cache/$1.html]

但是,如果不能让服务器端脚本工作,那么如何读取这个环境变量呢?一个解决方案是设置一个头文件:

Header set TEST_FOOBAR "%{REDIRECT_TEST0}e"

接受格式说明符,包括环境变量的%{NAME}e说明符(不要忘记小写e)。有时,你需要添加REDIRECT_前缀,但我还没有计算出什么时候添加前缀,什么时候不添加前缀。

我把这个留在这里,也许是明显的细节,但让我敲了几个小时的头: 小心使用%{REQUEST_URI},因为@克里斯特·范·贝西安在他的答案中说的是完全正确的,而不是REQUEST_URI字符串,因为这个TestString的输出以/开始。所以小心:

RewriteCond %{REQUEST_URI} ^/assets/$
^
| check this pesky fella right here if missing

如果你计划在. htaccess中编写多行规则,

.不要想尝试那些热修复方法来调试它 我浪费了好几天的时间设置了多条规则,没有从日志中得到反馈,最后才放弃 我在我的PC上安装了Apache,将整个站点复制到它的硬盘上,并使用日志快速整理了整个规则集 然后我回顾了我的旧规则,这些规则一直在起作用。我发现他们并没有真正按照要求去做。一个定时炸弹,给出一个稍微不同的地址。

重写规则中有太多的坑,这根本不是一个直接的逻辑问题 你可以在十分钟内启动并运行Apache,它是10MB的,良好的许可证,*NIX/WIN/MAC就绪,甚至不需要安装 此外,检查服务器的标题行,如果Apache是旧的,请从存档中获取相同版本的Apache。我的OP仍然在2.0;

如果您正在创建重定向,请使用旋度进行测试,以避免浏览器缓存问题。 使用-I只获取http头信息。

.使用-L跟随所有重定向

我观察到的一些错误发生在写.htaccess

在多个规则中重复使用^(.*)$,使用^(.*)$会导致其他规则在大多数情况下无效,因为它在一次命中中匹配所有url。

因此,如果我们为这个url sapmle/url使用规则,它也会使用这个url sapmle/url/string


应该使用[L]标志来确保我们的规则已经完成了处理。


应该知道:

%n和$n的差异

%n%{RewriteCond}部分进行匹配,而$n%{RewriteRule}部分进行匹配。

RewriteBase的Working . RewriteBase的Working

RewriteBase指令指定使用的URL前缀 重写规则(RewriteRule)指令 相对路径. < / p > 类中使用相对路径时,此指令是必需的 在每个目录(htaccess)上下文中的替换,除非任何 以下条件为真:

原始请求和替换在 DocumentRoot(而不是通过其他方式,如别名)。 包含重写规则的目录的文件系统路径, 以相对替换作为后缀的URL路径也是有效的 服务器(这很少见)。在Apache HTTP Server 2.4.16及更高版本中, 当请求通过Alias或 mod_userdir . < / p >

正如@JCastell所指出的,在线测试仪在测试针对.htaccess文件的个人重定向方面做得很好。然而,更有趣的是暴露的api,它可以用于使用json对象批量测试url列表。然而,为了使它更有用,我写了一个小的Bash脚本文件,它使用旋度金桥来提交一个url列表,并将json响应解析为一个CSV格式的输出,与htaccess文件中的行号和规则匹配,以及重定向url,使得比较电子表格中的url列表非常方便,并快速确定哪些规则是无效的。

如果你正在使用url,你可能想要检查是否“启用Mod重写”

如果你不是在一个标准的共享托管环境中工作,而是在一个你有管理权限的环境中工作(可能是你的本地测试环境),请确保启用了.htaccessmod_rewrite的使用。它们在默认Apache安装中被禁用。在这种情况下,在.htaccess文件中配置的任何操作都不起作用,即使正则表达式完全有效。

要允许使用.htaccess:

找到文件apache2.conf,在Debian/Ubuntu中,这是在/etc/apache2中,在文件中的section

<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

并将行AllowOverride None改为AllowOverride All

启用模块mod_rewrite:

在Debian/Ubuntu上执行

sudo a2enmod rewrite

顺便说一下,要禁用一个模块,你可以使用a2dismode而不是a2enmode

完成上述配置更改后,重新启动Apache使其生效:

sudo systemctl restart apache2

最好的调试方法!

LogLevel notice rewrite:trace8添加到apache的httpd.conf中,以记录mod_rewrite的所有通知。如果你在共享主机上,不能访问httpd.conf,那么在本地测试它并上传到现场。一旦启用这将在很短的时间内生成一个非常大的日志,这意味着它不能在活跃的服务器上进行测试。

也许调试重写规则的最好方法是根本不使用重写规则,而是将URL处理从htaccess文件推迟到PHP文件(让我们称之为router.php)。然后,您可以使用PHP进行任何您喜欢的操作,并使用适当的错误检测和常规的调试方法。这甚至运行得更快,因为您不必使用重写模块。

对于文件系统中没有的URL,要立即将控制权从.htaccess转移到router.php,只需在.htaccess中放入以下一行:

FallbackResource router.php

是的,真的很简单。是的,它确实有效。试一试。

注意:你可能需要在你的。htaccess文件中使用一个ErrorDocument指令来在HTTP状态404时显式地将某些url的控制权传递给你的router.php文件,特别是如果你继承了处理状态404的父htaccess文件。这样一来,将控制权传递到路由器文件总共需要两行。