debugging.htaccess重写规则的提示

许多海报在其.htaccess文件中debuggingRewriteRule和RewriteCond语句时遇到问题。 其中大部分都使用共享托pipe服务,因此无法访问根服务器configuration。 他们无法避免使用.htaccess文件进行重写, 也无法启用RewriteLogLevel“,许多受访者表示,还有许多.htaccess特有的缺陷和约束没有被很好的覆盖,build立一个本地testingLAMP栈涉及太多学习曲线最多。

所以我的Q在这里是我们如何build议他们自己debugging他们的规则。 我在下面提供一些build议。 其他build议,将不胜感激。

  1. 了解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 

    所以你的规则将被重复执行,如果你改变了URIpath,那么它可能会最终执行其他.htaccess文件(如果它们存在的话)。 所以确保你终止这个循环,如果需要的话join额外的RewriteCond来停止规则的触发。 除非明确地使用多级规则集,否则删除任何较低级别的.htaccess重写规则集。

  2. 通过对一组testing模式进行testing,确保每个正则expression式的语法是正确的 ,以确保这是一个有效的语法,并完成您想要的全部testingURI。 请参阅下面的答案了解更多详情

  3. 在testing目录中逐步构build您的规则。 您可以使用“执行最深的pathfunction.htaccess文件”设置一个单独的testing目录(树)和debugging规则集在这里没有搞砸你的主要规则,并停止您的网站的工作。 您必须一次添加一个,因为这是将故障本地化为单个规则的唯一方法。

  4. 使用虚拟脚本存根来转储出服务器和环境variables 。 (请参见清单2 )如果您的应用程序使用blog/index.php那么您可以将其复制到test/blog/index.php并使用它来testing您的博客规则在test子目录中。 您也可以使用环境variables来确保重写引擎正确地解释replacestring,例如

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

    并在phpinfo转储中查找这些REDIRECT_ *variables。 顺便说一句,我用这一个,发现我的网站,我不得不使用%{ENV:DOCUMENT_ROOT_REAL} 。 在redirect器循环的情况下, REDIRECT_REDIRECT_ *variables列出了前一个通道。 等等..

  5. 确保你不会被你的浏览器caching不正确的301redirect咬伤 。 见下面的答案 。 我感谢Ulrich Palha的这个。

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

还有什么意见或build议?

清单1 – phpinfo

 <?php phpinfo(INFO_ENVIRONMENT|INFO_VARIABLES); 

以下是关于testing规则的一些附加提示,可以减轻共享主机上用户的debugging

1.使用假用户代理

在testing新规则时,添加一个条件以仅使用您将用于请求的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,则可以使用User Agent Switcher创build伪造的用户代理string并进行testing。

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

我看过这么多post,人们还在testing他们的规则,他们使用的是301的。 不要

如果您的网站上没有使用build议1,则不仅您,而且当时访问您网站的任何人都将受到301的影响。

请记住,它们是永久性的,并被您的浏览器积极地caching。 直到你确定使用302,然后将其更改为301。

3.请记住,301是积极caching在您的浏览器

如果您的规则不起作用,而且看起来不错,而且您没有使用build议1和2,则在清除浏览器caching或私人浏览时重新进行testing。

4.使用HTTP捕获工具

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

而其他人可能会说,你的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 A, were 302 redirected to URL B and 301 redirected to URL C 即使URL C是最终的目标,你也会知道这对于search引擎优化是不利的,需要修复。

您将能够看到在服务器端设置的caching标头,重播请求,修改请求标头以testing….


在线.htaccess重写testing

我发现这个谷歌search正则expression式的帮助,它节省了我很多时间上传新的.htaccess文件,每次我做一个小的修改。

来自网站:

htaccesstesting仪

为了testing你的htaccess重写规则,只需填写你正在应用规则的url,将htaccess的内容放在更大的input区域,然后按“立即检查”button。

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

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

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

确保每个正则expression式的语法是正确的

通过对一组testing模式进行testing来确保这是一个有效的语法,并且完全按照您的testingU​​RI范围进行。

请参阅下面的regexpCheck.php以获得一个简单的脚本,您可以将其添加到站点中的私有/testing目录以帮助您执行此操作。 我保持这个简短而不是漂亮。 只要把它放到一个testing目录下的regexpCheck.php文件中,就可以在你的网站上使用它。 这将帮助您build立任何正则expression式,并在testing用例列表中进行testing。 我在这里使用PHP PCRE引擎,但是看过Apache的源代码,这和Apache中使用的基本相同。 有许多HowTos和教程提供模板,并可以帮助您build立您的正则expression式技能。

清单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> 

设置环境variables并使用标题接收它们:

您可以使用RewriteRule行创build新的环境variables,如OP所述:

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

但是,如果你不能得到一个服务器端脚本的工作,你怎么读取这个环境variables? 一种解决方法是设置一个标题:

 Header set TEST_FOOBAR "%{REDIRECT_TEST0}e" 

该值接受格式说明符 ,包括环境variables的%{NAME}e说明符(不要忘记小写字母e)。 有时候,您需要添加REDIRECT_前缀,但是在添加前缀时以及不添加时,我还没有弄清楚。

一个从我浪费了几个小时:

如果您应用了所有这些技巧,并且由于您无法访问服务器错误日志而只能执行500次错误,可能问题不在于.htaccess中,而是在redirect到的文件中。

在修复了.htaccess问题后,我花了两个多小时的时间尝试修复它,尽pipe我只是忘记了一些权限。

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

它是%{HTTP_HOST}而不是 ${HTTP_HOST} 。 error_log中没有任何内容,将不会有内部服务器错误,您的正则expression式仍然正确,规则将不匹配。 如果你使用django / genshi模板很多,并且在肌肉记忆中有variablesreplace,那么这真的很糟糕。

我试图debugging我的mod_rewrite问题时发现这个问题,它肯定有一些有用的build议。 但最终最重要的是确保你的正则expression式语法正确。 由于我自己的RE语法的问题,安装regexpCheck.php脚本不是一个可行的select。

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

只要input您的正则expression式和一个或多个示例URL,它会告诉您正则expression式是否匹配(“〜=”列中的“1”),如果适用,任何匹配的组(“拆分”列将对应于Apache期望的数字,例如$ 1,$ 2等)。 他们声称PCRE的支持是“testing版”,但这正是我需要解决我的语法问题。

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

我只是简单地给现有的答案添加评论,但是我的声望尚未达到这个水平。 希望这有助于某人。

对于4.,你仍然需要确保你的“虚拟脚本存根”实际上是所有重写完成后的目标URL,否则你什么也看不到!

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

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

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

这将在您将其插入规则集的位置停止重写,而不像debugging器中的断点。

如果您使用Apache <2.3.9,则需要使用[L]而不是[END] ,然后您可能需要添加:

 RewriteRule ^show.php$ - [L] 

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

如果您正在创buildredirect,请使用curl进行testing以避免浏览器caching问题。 使用-I只能获取http标头。 使用-L来关注所有redirect。

我在写.htaccess时发现了一些错误

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

所以,如果我们使用这个url sapmle/url规则,它也会消耗这个url sapmle/url/string


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


应该知道:

%n和$ n的区别

%n%{RewriteCond}部分中匹配, $n%{RewriteRule}部分中匹配。

RewriteBase的工作

RewriteBase指令指定用于replace相对path的每个目录(htaccess)RewriteRule指令的URL前缀。

除非满足以下任何一个条件,否则在每个目录(htaccess)上下文中的replace中使用相对path时,此指令是必需的:

原始请求和replace位于DocumentRoot之下(而不是通过别名等其他方法可访问)。 包含RewriteRule的目录的文件系统path(后缀为相对replace)在服务器上也是有效的(这很less见)。 在Apache HTTP Server 2.4.16和更高版本中,当通过Alias或mod_userdir映射请求时,可以省略该指令。

(类似Doin的想法)为了显示什么是匹配,我使用这个代码

 $keys = array_keys($_GET); foreach($keys as $i=>$key){ echo "$i => $key <br>"; } 

将它保存到服务器根目录下的r.php,然后在.htaccess中做一些testing
例如,我想匹配不以语言前缀开头的url

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

我会在这里留下这个,也许是很明显的细节,但是让我感到头痛了好几个小时:小心使用%{REQUEST_URI}因为@Krist van Besien在他的回答中说的是完全正确的, 而不是REQUEST_URIstring ,因为输出这个TestString开始于一个/ 。 所以保重:

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

如果您计划在.htacesss中写入不止一行规则,
甚至不要考虑尝试其中一种热修复方法来进行debugging。

我浪费了几天的时间来制定多条规则,而没有LOGs的反馈,只是最后放弃了一条规则。
我在我的电脑上安装了Apache,将整个站点复制到硬盘上,并且使用日志真正快速地整理了整个规则集。
然后,我回顾了我的旧规则,我发现他们并没有真正做到自己想做的事情。 一个有点不同的地址的定时炸弹。

重写规则有如此多的陷阱,这根本不是一个逻辑上的东西。
您可以在10分钟内启动并运行Apache,即使没有安装,也可以使用10MB的许可证,*准备好NIX / WIN / MAC。
另外,检查你的服务器的标题行,如果它是旧的,从档案中得到相同版本的Apache。 我的OP还在2.0上,很多东西都不支持。