按照预期 ,在htaccess中重写下面这个HTTPS请求不会被重写:
https://example.com/system/anything
不重写
http://example.com/system/anything
但是,意外的是,这个HTTPS请求被重写了:
https://example.com/preview/anything
被重写
http://example.com/index.php/preview/anything
为什么是这样?
一些其他的事实/意见:
/system/是服务器上的实际path。 但是/preview/不是一个真正的path – 这是一个在CMS中有意义的URL段,例如,/ /index.php/preview/anything /preview/anything是CMS如何获取/preview/anything URL的请求。
其他非系统的URL会正确地重写(从HTTPS到HTTP),并正确传递给index.php。 例如,
https://example.com/real
被重写
http://example.com/real
这是一整套规则:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / # Force HTTPS for System URLs RewriteCond %{REQUEST_URI} ^/system(.*)$ [NC] RewriteCond %{HTTPS} !=on RewriteRule ^(.*)$ "https://example.com/$1" [R=301,L] # Force HTTP for Other URLs, but not: system or preview RewriteCond %{REQUEST_URI} !^/(system|preview)/(.*)$ [NC] RewriteCond %{HTTPS} =on RewriteRule ^(.*)$ "http://example.com/$1" [R=302,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /index.php/$1 [L] </IfModule>
对于为什么/preview/得到奇怪的待遇的任何洞察力?
补充:请注意/preview/anything得到302redirect到/index.php/preview/anything – 这是什么似乎很奇怪/意外的一大部分。 它不应该在最终规则中得到redirect,只是重写。
这些重写规则是否在.htaccess文件中? 在这种情况下, [L]标志不会做你认为它做的事情 – 停止处理当前的规则集,但是再次使用适合于重写的URI的.htaccess文件处理请求,因此你的规则可能是再次执行。 这不会发生在Apacheconfiguration文件(而不是在一个<Directory>部分)的规则 – 在这种情况下, [L]标志按预期处理。
举个例子, https://example.com/preview/anything被内部的第三条规则改写成https://example.com/index.php/preview/anything 。 然而,为了处理这个请求,Apache必须再次读取.htaccess文件 – 这次URI匹配你的第二个规则,它返回一个302redirect。
Apache 2.4.x 支持[END]标志,它停止这样的重写循环,不像[L] ; 早期Apache版本的解决scheme更为复杂。
如果你想确保Apache只对你的重写规则进行一次传递,你可以在所有其他规则之前添加下面的规则:
RewriteCond %{ENV:REDIRECT_STATUS} !="" RewriteRule ^ - [L]
在第一次通过REDIRECT_STATUS将是空的; 在第二次通过时,它将有一个非空值(通常是200 ),规则将匹配,并确实停止进一步的重写。
如果这样的规则不合适(例如,在某些情况下,您需要在第二遍中处理重写的URI),则可以在规则中设置一个应该是最终的环境variables:
RewriteRule ^(.*)$ /index.php/$1 [L,E=FINISH:1]
并在所有其他人之前添加以下规则:
RewriteCond %{ENV:REDIRECT_FINISH} !="" RewriteRule ^ - [L]
请注意,在第二次传递期间,Apache会将REDIRECT_设置为在第一次传递期间定义的环境variables名称,因此您需要设置FINISH ,但testingREDIRECT_FINISH 。
或者,您可以尝试修改您的匹配条件,以便在第二遍中修改的URI不会与第二个规则中的正则expression式匹配(例如,将(index\.php/)?插入到正则expression式中)。
这听起来像最后一个规则是在完成时引入绝对的基于http的redirect,可能是由于Apache或mod_rewrite中的一些错误或function。
你如何把它分解成两个规则,并有一个额外的条件?
尝试这个:
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{HTTPS} =off RewriteRule ^(.*)$ http://example.com/index.php/$1 [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{HTTPS} =on RewriteRule ^(.*)$ https://example.com/index.php/$1 [L]
是httpscheme(而不是https ),困扰你吗? 如果是这样,你应该看看RewriteBase指令。
从mod_rewrite文档 :
RewriteBase指令指定用于replace相对path的每个目录(htaccess)RewriteRule指令的URL前缀。在每个目录(htaccess)上下文(…)中的replace中使用相对path时,此指令是必需的
我不认为你需要在RewriteCond语句结尾处的'(。*)$',因为你没有在任何地方使用捕获的数据。 你可以像这样简化这两个:
RewriteCond %{REQUEST_URI} ^/system [NC]
和RewriteCond%{REQUEST_URI}!^ /(系统|预览)/ [NC]
我还build议打开RewriteLog并设置RewriteLogLevel以查看每个请求的Apache正在做什么。 使用apache 2.2或更less,将是:
RewriteLog /var/log/apache2/rewrite.log RewriteLogLevel 7
你应该一步一步看到匹配的是什么,以及Apache采取了什么动作。