奇怪的apache mod_rewrite行为

我是新来的网站,我希望你能帮我解决一个我想要安装Rhodecode的问题。 这是(长)故事:
我已经设法在一个linux盒子里的virtualenv里面安装Rhodecode。 使用开发服务器( paster serve production.ini ),我看到它运行完美。 但是,我想使用Apache作为SSL的前端,使用mod_rewrite将http请求redirect到https。 这是我的configuration:

默认vhost.conf

 <VirtualHost _default_:80> ServerName hg.mydomain.com ServerAdmin [email protected] ServerAlias rhodecode.mydomain.com DocumentRoot "/srv/www/htdocs" RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] HostnameLookups Off UseCanonicalName Off ServerSignature Off .... </VirtualHost> 

我使用mod_rewrite而不是redirect,因为我希望我的网站可以被两个域名访问。 我们在hg.mydomain.com有一个网站,我们打算用新的rhodecode.mydomain.comreplace,所以我想在重写规则中保留主机名。 有了这个指令:

 Redirect permanent / https://hg.mydomain.com/ 

该网站运作完美,没有redirect问题发生。 但是,当我浏览到http://rhodecode.mydomain.com ,我被redirect到其他网站,我不能这样做,直到hg.mydomain.com网站被丢弃,hg.mydomain.com指向与rhodecode.mydomain.com相同的IP。

问题
Rhodecode有时会包含对要求身份validation的操作的响应。 例如,如果你是一个客人,并尝试一个私人回购,你被redirect到login屏幕,像这样的url:

 https://rhodecode.mydomain.com/_admin/login?came_from=%2F 

%2F是编码的“/”。
login后,我redirect到https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var ,并显示404错误的apache默认页面。 之后,浏览到https://rhodecode.mydomain.com/显示我正在会话中的网站。 为什么我redirect到那个奇怪的HTTP_NOT_FOUND.html.var文件? 以下是我的configuration的其余部分和日志的相关部分:

默认的虚拟主机,ssl.conf中

 <VirtualHost _default_:443> ServerName hg.mydomain.com ServerAdmin [email protected] ServerAlias rhodecode.mydomain.com DocumentRoot "/srv/www/htdocs" HostnameLookups Off UseCanonicalName Off ServerSignature Off SSLEngine on certificate stuff ... WSGIDaemonProcess hg.mydomain.com user=rhodecode group=users threads=5 \ home=/home/rhodecode/rhodecode-env python-path=/home/rhodecode/rhodecode-env/lib/python2.7/site-packages WSGIScriptAlias / /home/rhodecode/rhodecode-env/dispatch.wsgi WSGIPassAuthorization On <Directory /home/rhodecode/rhodecode-env> WSGIProcessGroup hg.mydomain.com WSGIApplicationGroup %{GLOBAL} Order deny,allow Allow from all </Directory> </VirtualHost> 

重写日志

 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (2) init rewrite engine with requested uri /error/HTTP_NOT_FOUND.html.var 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (3) applying pattern '(.*)' to uri '/error/HTTP_NOT_FOUND.html.var' 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (4) RewriteCond: input='off' pattern='off' => matched 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (2) rewrite '/error/HTTP_NOT_FOUND.html.var' -> 'https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var' 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (2) explicitly forcing redirect with https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (1) escaping https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var for redirect 172.17.1.49 - - [04/Mar/2014:00:06:24 +0000] [rhodecode.mydomain.com/sid#7f6a03266f00][rid#7f69fd68d7a0/initial/redir#1] (1) redirect to https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var [REDIRECT/301] 

注意摆脱#7f69fd68d7a0 / initial / redir#1部分。 当我发送没有%2F的URL时,该部分不会出现在日志中。

访问日志

 hg.mydomain.com:443 172.17.1.49 - - [04/Mar/2014:02:09:13 +0000] "POST /_admin/login?came_from=%252F HTTP/1.1" 302 186 "https://rhodecode.mydomain.com/_admin/login?came_from=%252F" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36" hg.mydomain.com:80 172.17.1.49 - - [04/Mar/2014:02:09:14 +0000] "GET /_admin/%2F HTTP/1.1" 301 268 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36" hg.mydomain.com:443 172.17.1.49 - - [04/Mar/2014:02:09:14 +0000] "GET /error/HTTP_NOT_FOUND.html.var HTTP/1.1" 200 1132 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36" hg.mydomain.com:443 172.17.1.49 - - [04/Mar/2014:02:09:14 +0000] "GET /favicon.ico HTTP/1.1" 404 618 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36" 

第一行是带有validation数据的POST请求。 它是成功的,所以它redirect到pipe理页面。 在第二行中,您可以看到redirect位置将我发回到http(它进入端口80)到/ _admin /%2F,因此redirect到https,但是魔术般地转换为/error/HTTP_NOT_FOUND.html.var

HTTP请求 (为了简洁,一些头文件被忽略)

 POST /_admin/login?came_from=%252F HTTP/1.1 Host: rhodecode.mydomain.com Cache-Control: no-cache Pragma: no-cache Origin: https://rhodecode.mydomain.com Content-Type: application/x-www-form-urlencoded Referer: https://rhodecode.mydomain.com/_admin/login?came_from=%252F Cookie: rhodecode=3af58050ce87a93caa5a4c6809c5dacef4afb29d8e74b152c97f469199c554b6f67f7aa7 ... 

HTTP响应

 HTTP/1.1 302 Found Date: Tue, 04 Mar 2014 02:24:11 GMT Server: Apache/2.2.22 (Linux/SUSE) Pragma: no-cache Cache-Control: no-cache Set-Cookie: rhodecode=f0a94a155738490da032b46354f4d72338902da2d69bc1177bcf4086aa8158f4719526e0; httponly; Path=/ Location: http://rhodecode.mydomain.com/_admin/%2F ... 

HTTP请求2

 GET /_admin/%2F HTTP/1.1 Host: rhodecode.mydomain.com Cache-Control: no-cache Pragma: no-cache Cookie: rhodecode=f0a94a155738490da032b46354f4d72338902da2d69bc1177bcf4086aa8158f4719526e0 ... 

HTTP响应2

 HTTP/1.1 301 Moved Permanently Date: Tue, 04 Mar 2014 02:24:12 GMT Server: Apache/2.2.22 (Linux/SUSE) Location: https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var ... 

最后,试图得到/error/HTTP_NOT_FOUND.html.var不给我一个404错误,但200 OK响应!
我认为浏览器在底层做了一些奇怪的事情,所以我发送了一个原始的HTTP请求,得到了相同的结果:

 [Rober@yue ~]$ nc rhodecode.mydomain.com 80 GET /%2F HTTP/1.1 Host: rhodecode.mydomain.com HTTP/1.1 301 Moved Permanently Date: Tue, 04 Mar 2014 00:07:26 GMT Server: Apache/2.2.22 (Linux/SUSE) Location: https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var Content-Length: 268 Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>301 Moved Permanently</title> </head><body> <h1>Moved Permanently</h1> <p>The document has moved <a href="https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var">here</a>.</p> </body></html> 

查看响应中的位置标题。 为什么Apache这样做,而不是只是改变请求到HTTPS?

对不起我的问题的looong文本,但我想尽可能多的信息,所以你可以帮我debugging这:)

感谢所有提前!

编辑
正如所build议的,我想知道Rhodecode是否正在发送redirect。 所以我改变了我在文档中显示的脚本: http://modwsgi.readthedocs.org/en/latest/configuration-guides/running-a-basic-application.html#wsgi-application-script-file : http://modwsgi.readthedocs.org/en/latest/configuration-guides/running-a-basic-application.html#wsgi-application-script-file 。 它总是返回一个“Hello World”,而没有redirect的可能性。 我向网站发送了一个原始请求,结果相同,所以Apache必须以某种方式更改url。 结果如下:

 GET /%2F HTTP/1.1 Host: rhodecode.mydomain.com HTTP/1.1 301 Moved Permanently Date: Thu, 06 Mar 2014 04:23:31 GMT Server: Apache/2.2.22 (Linux/SUSE) Location: https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var Content-Length: 268 Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>301 Moved Permanently</title> </head><body> <h1>Moved Permanently</h1> <p>The document has moved <a href="https://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var">here</a>.</p> </body></html> 

但是,当我发送一个正常的URL(没有%2F),它redirect确定:

 GET /abffr HTTP/1.1 Host: rhodecode.mydomain.com HTTP/1.1 301 Moved Permanently Date: Thu, 06 Mar 2014 04:25:19 GMT Server: Apache/2.2.22 (Linux/SUSE) Location: https://rhodecode.mydomain.com/abffr Content-Length: 244 Content-Type: text/html; charset=iso-8859-1 

Apache似乎不喜欢%2F的东西…

原来有两件事是造成这个错误。 首先是AllowEncodedSlashes的默认值,从文档 :

 With the default value, Off, URLs which contain encoded path separators (%2F for / and additionally %5C for \ on according systems) are refused with a 404 (Not found) error. 

我通过打开apache日志中的最大冗余来发现这一点,并发现:

 [Mon Mar 10 04:53:43 2014] [info] [client 172.17.1.49] found %2f (encoded '/') in URI (decoded='//'), returning 404 

因此,我所有的%2F请求都被拒绝了。 此外,我有这个configuration默认出现在我的服务器上:

 <IfModule mod_negotiation.c> <IfModule mod_include.c> <Directory "/usr/share/apache2/error"> AllowOverride None Options IncludesNoExec AddOutputFilter Includes html AddHandler type-map var Order allow,deny Allow from all LanguagePriority en cs de es fr it ja ko nl pl pt-br ro sv tr ForceLanguagePriority Prefer Fallback </Directory> ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var ErrorDocument 410 /error/HTTP_GONE.html.var ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var </IfModule> </IfModule> 

因此,每次向客户端发送404错误时,包含filter与ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var指令相结合ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var导致redirect发送到浏览器,以访问http://rhodecode.mydomain.com/error/HTTP_NOT_FOUND.html.var
您可以在我的重写日志中看到此行为,其中要重写的expression式使用/error/HTTP_NOT_FOUND.html.var进行初始化:

 172.17.1.49 - - [10/Mar/2014:04:04:23 +0000] [rhodecode.mydomain.com/sid#7f98fedd7f00][rid#7f98f91fe7a0/initial/redir#1] (2) init rewrite engine with requested uri /error/HTTP_NOT_FOUND.html.var 

另请注意, redir#1表示发生内部redirect。 所以,我改变了我的configuration

 RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] AllowEncodedSlashes NoDecode 

并将ifmodule指令更改为

 <IfModule !mod_include.c> 

所以没有ErrorDocument指令被执行,一切工作完美!
这一次,重写日志在url中显示%2F并且没有发生redir事件:

 172.17.1.49 - - [10/Mar/2014:04:55:40 +0000] [rhodecode.mydomain.com/sid#7f9fd1f83f00][rid#7f9fcc3a90a0/initial] (2) init rewrite engine with requested uri /%2F 

RhodeCode还有一个特殊的标志来告诉它你想强制使用ssl,这意味着所有的redirect都会一直到https。

在你的.ini旗帜变化中:

force_ssl = true