Many posters have problems debugging their RewriteRule and RewriteCond statements within their .htaccess files. Most of these are using a shared hosting service and therefore don't have access to the root server configuration. They cannot avoid using .htaccess files for rewriting and cannot enable a RewriteLogLevel" as many respondents suggest. Also there are many .htaccess-specific pitfalls and constraints are aren't covered well. Setting up a local test LAMP stack involves too much of a learning curve for most.

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

Understand that the mod_rewrite engine cycles through .htaccess files. The engine runs this loop: 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 So your rules will get executed repeatedly and if you change the URI path then it may end up executing other .htaccessfiles if they exist. So make sure that you terminate this loop, if necessary by adding extra RewriteCond to stop rules firing. Also delete any lower level .htaccess rewrite rulesets unless explicitly intent to use multi-level rulesets. Make sure that the syntax of each Regexp is correct by testing against a set of test patterns to make sure that is a valid syntax and does what you intend with a fully range of test URIs. See answer below for more details. Build up your rules incrementally in a test directory. You can make use of the "execute the deepest .htaccess file on the path feature" to set up a separate test directory (tree) and debug rulesets here without screwing up your main rules and stopping your site working. You have to add them one at a time because this is the only way to localise failures to individual rules. Use a dummy script stub to dump out server and environment variables. (See Listing 2)If your app uses, say, blog/index.php then you can copy this into test/blog/index.php and use it to test out your blog rules in the test subdirectory. You can also use environment variables to make sure that the rewrite engine in interpreting substitution strings correctly, e.g. RewriteRule ^(.*) - [E=TEST0:%{DOCUMENT_ROOT}/blog/html_cache/$1.html] and look for these REDIRECT_* variables in the phpinfo dump. BTW, I used this one and discovered on my site that I had to use %{ENV:DOCUMENT_ROOT_REAL} instead. In the case of redirector looping REDIRECT_REDIRECT_* variables list the previous pass. Etc.. Make sure that you don't get bitten by your browser caching incorrect 301 redirects. See answer below. My thanks to Ulrich Palha for this. The rewrite engine seems sensitive to cascaded rules within an .htaccess context, (that is where a RewriteRule results in a substitution and this falls though to further rules), as I found bugs with internal sub-requests (1), and incorrect PATH_INFO processing which can often be prevents by use of the [NS], [L] and [PT] flags.

还有什么评论或建议吗?

清单1——phpinfo

<?php phpinfo(INFO_ENVIRONMENT|INFO_VARIABLES);

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

1. 使用伪用户代理

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

e.g

#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捕获工具

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

虽然其他人可能会说您的网站看起来不正确,但您可以看到并报告所有的图像、css和js都返回404错误,从而迅速缩小问题范围。

虽然其他人会报告说你从URL A开始,结束于URL C,你将能够看到他们从URL A开始,被302重定向到URL B和301重定向到URL C。即使URL C是最终目标,你也会知道这对SEO是不利的,需要修复。

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



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

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

See regexpCheck.php below for a simple script that you can add to a private/test directory in your site to help you do this. I've kept this brief rather than pretty. Just past this into a file regexpCheck.php in a test directory to use it on your website. This will help you build up any regexp and test it against a list of test cases as you do so. I am using the PHP PCRE engine here, but having had a look at the Apache source, this is basically identical to the one used in Apache. There are many HowTos and tutorials which provide templates and can help you build your regexp skills.

清单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},因为@Krist van Besien在他的回答中说的是完全正确的,但不是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;很多东西是不支持的。


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


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

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

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


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


应该知道:

%n和$n的差值

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

RewriteBase的工作

The RewriteBase directive specifies the URL prefix to be used for per-directory (htaccess) RewriteRule directives that substitute a relative path. This directive is required when you use a relative path in a substitution in per-directory (htaccess) context unless any of the following conditions are true: The original request, and the substitution, are underneath the DocumentRoot (as opposed to reachable by other means, such as Alias). The filesystem path to the directory containing the RewriteRule, suffixed by the relative substitution is also valid as a URL path on the server (this is rare). In Apache HTTP Server 2.4.16 and later, this directive may be omitted when the request is mapped via Alias or mod_userdir.


as pointed out by @JCastell, the online tester does a good job of testing individual redirects against an .htaccess file. However, more interesting is the api exposed which can be used to batch test a list of urls using a json object. However, to make it more useful, I have written a small bash script file which makes use of curl and jq to submit a list of urls and parse the json response into a CSV formated output with the line number and rule matched in the htaccess file along with the redirected url, making it quite handy to compare a list of urls in a spreadsheet and quickly determine which rules are not working.


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


如果你不是在一个标准的共享主机环境中工作,而是在一个你有管理权限的环境中工作(可能是你的本地测试环境),确保启用了.htaccess和mod_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

最好的调试方法!

在apache的httpd.conf文件中增加LogLevel notice rewrite:trace8,记录所有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文件。这样一来,将控制权传递到路由器文件总共需要两行。