【问题标题】:Tips for debugging .htaccess rewrite rules调试 .htaccess 重写规则的提示
【发布时间】:2012-02-27 12:34:04
【问题描述】:

许多发帖人在调试他们的 .htaccess 文件中的 RewriteRule 和 RewriteCond 语句时遇到问题。其中大多数使用共享托管服务,因此无权访问根服务器配置。正如许多受访者所建议的那样,他们无法避免使用.htaccess 文件进行重写,并且无法启用 RewriteLogLevel”。此外,还有许多.htaccess 特有的陷阱和约束没有得到很好的覆盖。设置一个对于大多数人来说,本地测试 LAMP 堆栈涉及太多的学习曲线。

所以我的问题是我们如何建议他们调试他们的规则他们自己。我在下面提供一些建议。其他建议将不胜感激。

  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
    

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

  2. 通过针对一组测试模式进行测试,确保每个 Regexp 的语法正确,以确保它是有效的语法,并且可以使用完整的测试 URI 实现您想要的。详情请见answer below

  3. 在测试目录中逐步建立规则。您可以利用“在路径上执行最深的.htaccess文件功能”来设置单独的测试目录(树)和在此处调试规则集,而不会破坏您的主要规则并停止您的站点工作。您必须一次添加一个,因为这是将故障定位到单个规则的唯一方法。

  4. 使用虚拟脚本存根转储服务器和环境变量。 (参见清单 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. 确保您不会被浏览器缓存不正确的 301 重定向所困扰。见answer below。我感谢Ulrich Palha

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

还有什么意见或建议吗?

清单 1 -- phpinfo

<?php phpinfo(INFO_ENVIRONMENT|INFO_VARIABLES);

【问题讨论】:

  • 这些很好...也许您应该将它们从问题移到答案中。
  • @w00t,我已根据您的建议拆分了正则表达式检查器,因为我想通过链接在其他答案中引用它。
  • 您可能希望将the control flow diagram from the docs 添加到您的第一个建议中。 IMO 它比任何伪代码或解释都更容易理解,这确实是 mod-rewrite 巫术中最黑暗的部分。
  • 6 号是一笔大买卖。重写标准 apache 配置文件与 .htaccess 文件中行为不同的规则必须吸引很多人。
  • 在这些提示中可能值得添加的东西:我花了一些时间调试它重定向而不是重写的问题。事实证明,当我想要“/comment/”时,我将它重写为“/comment”。它正在重写到“/comment”,然后服务器正在重定向到“/comment/”。对于那些习惯于 Apache 的人来说,这种行为很明显,但对于像我这样的新手来说可能就不那么明显了。

标签: apache .htaccess mod-rewrite


【解决方案1】:

这里有一些关于测试规则的额外提示,可以帮助用户在共享主机上进行调试

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,则可以使用 User Agent Switcher 创建假用户代理字符串并进行测试。

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

我看过很多帖子,人们仍在测试他们的规则并且他们正在使用 301。 不要

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

请记住,它们是永久性的,并且会被您的浏览器主动缓存。 在确定之前使用 302,然后将其更改为 301。

3。请记住,301 会主动缓存在您的浏览器中

如果您的规则不起作用并且您认为它正确,并且您没有使用建议 1 和 2,请在清除浏览器缓存后或在无痕浏览时重新测试。

4。使用 HTTP 捕获工具

使用像Fiddler 这样的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 不利,需要修复。

您将能够查看在服务器端设置的缓存标头、重放请求、修改请求标头以进行测试 ....


【讨论】:

  • Ulrich,非常感谢您的意见。你发现了一些我没想到的方面。在 301 调试问题上,我在“私人浏览”(又名“色情模式”)中使用 Chrome,因为这会在您关闭窗口时转储此状态信息。我希望你不介意我不“接受”这是一个重要的观点,但不是一个最佳答案。再次感谢。 :)
  • 为了清楚起见(你的代码中确实有它但没有发现它),但要确保你使用的是 302 而不是 301 重定向,你需要 [L,R=302]
  • 您不需要显式指定[L, R=302] 只需执行[L,R] 默认为302
  • @goodeye,同时查看“Chrome > 设置 > 常规 > 在 DevTools 打开时禁用缓存”复选框。
【解决方案2】:

在线.htaccess重写测试

我发现 this 在谷歌上搜索 RegEx 帮助,它为我节省了很多时间,因为我不必在每次进行小修改时都上传新的 .htaccess 文件。

来自网站:

htaccess 测试器

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

【讨论】:

  • 感谢指向此工具的指针,我找到了调试问题的最直接方法。
  • 如果您可以通过 ssh 访问您的网站空间,另一种选择是直接通过服务器上的编辑器更改 .htaccess。
  • 您需要忽略 ssl 警告 b/c 站点证书有问题。但是网站还在。这是最好和最简单的解决方案。它提供了令人难以置信的洞察力,并有助于快速解决问题。
  • 感谢您指向此工具。这很有帮助,有时调试 apache htaccess 本身太难了。谢谢。非常感谢
  • 首先看起来不错。但它错过了很多功能。不幸的是,它不是一个可靠的工具。
【解决方案3】:

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

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

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

【讨论】:

  • 是的,输入到 RewriteRule 的字符串是相对的,因此在任何前导 / 上被剥离,但是对于在 Rewrite 中组装的匹配字符串不会发生这种剥离条件命令。
【解决方案4】:

设置环境变量并使用headers接收:

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

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

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

Header set TEST_FOOBAR "%{REDIRECT_TEST0}e"

accepts format specifiers,包括环境变量的%{NAME}e 说明符(不要忘记小写的e)。有时,您需要添加 REDIRECT_ 前缀,但我还没有弄清楚何时添加前缀以及何时不添加。

【讨论】:

  • 您是否对何时使用或不使用REDIRECT_ 前缀有了进一步的了解?此外,我在其他(htaccess)上下文中也看到了有关前缀的术语,但它的确切含义一直不清楚。这是否意味着在使用某些命令(但不是其他命令)时,您必须使用前缀命名变量,或者将前缀添加到命名变量?您的示例是 first 显示 both var 定义和 var 用法,因此我倾向于认为后者!文档帮助不大 - 他们认为我们知道的太多,提供的参考/链接太少。
【解决方案5】:

确保每个 Regexp 的语法正确

通过针对一组测试模式进行测试,以确保这是一个有效的语法,并且可以使用完整的测试 URI 来满足您的需求。

请参阅下面的 regexpCheck.php 以获得一个简单的脚本,您可以将其添加到您网站的私有/测试目录中以帮助您执行此操作。我一直保持简短而不是漂亮。只需将其放入测试目录中的文件regexpCheck.php 即可在您的网站上使用它。这将帮助您构建任何正则表达式,并在您这样做时根据测试用例列表对其进行测试。我在这里使用 PHP PCRE 引擎,但是查看了 Apache 源代码,这与 Apache 中使用的基本相同。有许多提供模板的 HowTos 和教程可以帮助您建立正则表达式技能。

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

【讨论】:

  • 快速说明:import_request_variables 在 PHP 5.3 中被弃用,在 5.4 中被删除。 extract($_GET) 加上extract($_POST) 可以执行相同的功能,但所有变量都需要从其名称中删除前缀。来源:php.net/manual/en/function.import-request-variables.php
  • @watcher,谢谢。一年前,我将本地版本更新为兼容 5.4,但忘记更改此帖子。现在完成了。
  • 哦,天哪,即使经过编辑,仅通过复制您的代码也无法获得良好的结果……但是随着正则表达式提琴手的出现,我认为您的工具无论如何都已经过时了。看看这些很酷的工具:regex101.comrefiddle.comregexr.com
  • @hexereisoftware,这篇文章已经有 3 年历史了,所以根据现在使用的 PHP 版本和 Apache 版本,可能会有一些细微的问题。然而,正则表达式有许多变体,每个变体都有细微的差别。正如我所说,Apache 代码使用与 PHP 引擎非常相似的 PCRE 引擎。我不确定与 .Net 等其他变体的差异是什么,所以虽然您建议使用在线资源是一个很好的建议,但我会坚持使用明确支持 apache 或 PHP 语法的。 :-)
  • Perl 最接近,但 php 使用相同的语法
【解决方案6】:

我浪费了几个小时:

如果您已经应用了所有这些提示并且由于您无权访问服务器错误日志而只出现 500 个错误,那么问题可能不在 .htaccess 中,而是在它重定向到的文件中。

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

【讨论】:

  • 我为我的个人网站使用共享访问托管网络服务,但我所做的是设置一个测试 VM,它在 PHP / Apache 配置、主目录等方面大致反映了这一点.. 但是因为这个虚拟机在我的管理员下,我可以启用重写日志来诊断任何困难的.htaccess 问题。
【解决方案7】:

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

%{HTTP_HOST}不是 ${HTTP_HOST}。 error_log 中不会有任何内容,不会有内部服务器错误,您的正则表达式仍然正确,只是规则不匹配。如果您经常使用 django / genshi 模板并在肌肉记忆中使用 ${} 进行变量替换,这真的很可怕。

【讨论】:

  • 是的,$ 替换变量与最后一个 RewriteRule 模式有关,而 % 与最后一个 RewriteCond 模式和特殊变量有关,例如 %{环境:XXX}
【解决方案8】:

如果您要创建重定向,请使用 curl 进行测试以避免浏览器缓存问题。 使用 -I 仅获取 http 标头。 使用 -L 跟踪所有重定向。

【讨论】:

    【解决方案9】:

    关于4.,你仍然需要确保你的“dummy script stub”在所有重写完成后实际上是目标URL,否则你什么都看不到!

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

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

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

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

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

    RewriteRule ^show.php$ - [L]
    

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

    【讨论】:

      【解决方案10】:

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

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

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

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

      我只是在现有答案中添加了评论,但我的声誉还没有达到那个水平。希望这对某人有所帮助。

      【讨论】:

      【解决方案11】:

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

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

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


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


      应该知道:

      Difference in %n and $n

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

      Working of RewriteBase

      RewriteBase 指令指定要用于的 URL 前缀 每个目录 (htaccess) 的 RewriteRule 指令替换 相对路径。

      当您使用相对路径时,需要此指令 每个目录(htaccess)上下文中的替换,除非任何 以下条件为真:

      原始请求和替换位于 DocumentRoot(而不是通过其他方式访问,例如 Alias)。 包含 RewriteRule 的目录的文件系统路径, 以相对替换为后缀的 URL 路径也有效 服务器(这种情况很少见)。在 Apache HTTP Server 2.4.16 及更高版本中, 当请求通过别名映射时,可以省略此指令或 mod_userdir。

      【讨论】:

        【解决方案12】:

        如果您打算在 .htacesss 中编写不止一行规则,
        甚至不要考虑尝试其中一种热修复方法来调试它。

        我浪费了几天时间设置多个规则,但没有来自 LOG 的反馈,最终还是放弃了。
        我在我的 PC 上安装了 Apache,将整个站点复制到它的 HDD,并使用日志整理了整个规则集,速度非常快。
        然后我回顾了我的旧规则,这些规则一直在起作用。我看到他们并没有真正做他们想要的。定时炸弹,地址略有不同。

        重写规则有这么多坑,这根本不是一个直截了当的逻辑。
        您可以在十分钟内启动并运行 Apache,它有 10MB,良好的许可证,*NIX/WIN/MAC 就绪,即使没有安装。
        此外,检查您的服务器的标题行,如果它是旧的,则从他们的存档中获取相同版本的 Apache。我的OP还在2.0;很多东西都不支持。

        【讨论】:

        • papo,我在我的开发结构中运行了专用服务器、ISP 托管的 VPS 和私有 VM,但我仍然为我的公共域和电子邮件使用共享托管服务,只是因为它更方便并且为这些使用完全托管的服务具有成本效益。这个howto真的是针对共享服务用户的。将私有 VM 配置为完全镜像共享服务是很困难的。是的,如果你可以使用测试 VM 会有所帮助,但我仍然不时在我的共享服务上使用这些“技巧”。
        • 我会同意这个 if 你的 A 被框定为调试mod_rewrite 规则的替代建议,但开头的“甚至不要考虑它”是对于那些难以理解为什么他们的htaccess 文件没有按照他们应该的方式工作的基本共享服务用户来说,这简直是一个糟糕的建议。
        • 很抱歉,如果听起来你收集大量建议的工作毫无价值。我不想那样。相信我,我很高兴阅读并遵循此线程提供的许多建议。但是我的规则慢慢变得复杂了,最后,我浪费了很多时间,只是不想经历安装 Apache 服务器的麻烦,并按照应有的方式进行调试。不仅如此,我什么也没学到,因为我没有在日志中看到真正发生的事情。还有很多事情正在发生。我相信分享这种经验也很有价值。
        • 对于第二部分,有一个 IF。我现在看到的文字从来没有以“不要想”开头,这个措辞似乎有点刺耳,但都是真的。特别是对于那些不熟悉并且难以理解的人。这里的建议可能会误导他们,就像我一样,我所需要的只是一个可靠的正则表达式,它不是那么简单,就像你的观点 6) PATH_INFO 给我带来了很多麻烦,而且它不是你所说的错误,而是一个特性。如果您不想重新添加它,请使用 [DPI]。但只有当您查看日志时,您才会看到它被添加到那里。这就是为什么,不止一行,你最好使用日志
        • 对不起@papo,但我投-1票的原因是我认为这个“不要考虑它”是个坏建议,IMO。如果您的观点是“超过一定的复杂性,那么您可能会发现安装本地 Apache 服务来调试您的 .htaccess 文件更容易”,那么这就更平衡了。是的,设置本地 Apache 服务相当容易,但是让它反映服务提供商的共享托管服务可能很复杂,而且超出了许多可能刚刚使用过 Wordpress 一键设置的用户的技能水平,说,他们的.htaccess 文件有问题。
        【解决方案13】:

        我将把这个留在这里,也许是明显的细节,但让我头疼了好几个小时: 使用%{REQUEST_URI} 时要小心,因为@Krist van Besien 在他的回答中说的是完全正确的,但不适用于 REQUEST_URI 字符串,因为此 TestString 的输出以/。所以要小心:

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

        【讨论】:

          【解决方案14】:

          (类似于 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
          

          【讨论】:

          • 只使用 phpinfo() 存根,正如我在 O/P 的第 4 点中提到的那样,基本上是相同的事情。寻找QUERY_STRING
          【解决方案15】:

          正如@JCastell 所指出的,online tester 在针对 .htaccess 文件测试单个重定向方面做得很好。然而,更有趣的是api 暴露,可用于使用 json 对象批量测试 url 列表。然而,为了让它更有用,我写了一个小的bash script file,它利用curljq提交一个url列表并将json响应解析为一个CSV格式的输出,其中行号和规则匹配在 htaccess 文件中连同重定向的 url 一起,使得比较电子表格中的 url 列表并快速确定哪些规则不起作用非常方便。

          【讨论】:

            【解决方案16】:

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

            启用.htaccess

            找到文件 apache2.conf,在 Debian/Ubuntu 上,它位于 /etc/apache2 中,并且在文件中的部分

            <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
            

            【讨论】:

              【解决方案17】:

              最好的调试方法!

              LogLevel notice rewrite:trace8 添加到apache 的httpd.conf 以记录mod_rewrite 的所有通知。如果您在共享主机上并且无权访问httpd.conf,请在本地对其进行测试并上传到实时站点。一旦启用,它会在很短的时间内生成一个非常大的日志,这意味着它无论如何都无法在实时服务器上进行测试。

              【讨论】:

                【解决方案18】:

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

                【讨论】:

                  猜你喜欢
                  • 2011-11-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多