Apache 2.4 mod_rewrite 规则将重定向应用于非重定向规则(仅 [L] 标志)

Apache 2.4 mod_rewrite 规则将重定向应用于非重定向规则(仅 [L] 标志)

因此,我在使用 Web 应用程序的 apache 2.4/CentOS7 上遇到了这个令人抓狂的问题。

我想将应用程序域上的所有非安全 URL 重定向到安全版本,除了一个 URI(供应商沙盒模式的 webhook 端点 - 没有有效的 SSL 证书,因为这不是生产环境,所以我们必须允许 HTTP)。除了应用程序域之外,还有一些客户站点域也通过此运行,因此我们不希望这些域重定向,因为没有一个域具有 SSL,因此我们明确匹配 HTTP_HOST。

这是一个前端控制器模式,我们希望index.php处理所有域的非文件和非目录的所有这些请求,并且仅处理应用程序域的 SSL 请求。

看起来很简单:

RewriteEngine on
RewriteBase "/"

# Redirect all app domain urls to ssl
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
  RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# I had to split this -d rule off of the -f rule to account for custom
# domain homepages from not being routed to the front controller when
# transitioning from centos6/apache2.2 for some reason
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} "!^/$"
  RewriteRule "^(.*)$" index.php [L]

# Redirect non-files to entry script
RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule "^(.*)$" index.php [L]

但是当我测试时发生了什么

curl http://app.example.com/AllowThisUri/Http

传递给的最后一条规则index.php正在被使用,但它并没有被实际处理index.php,而是重定向到那里... curl 返回(缩短的响应)

<title>301 Moved Permanently</title>
<p>The document has moved <a href="https://app.example.com/index.php">here</a>.</p>

因此,如果我注释掉这个新的排除RewriteCond

#RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"

预期的事情发生了,它重定向到相同的 URL,但使用了 https:

<title>301 Moved Permanently</title>
<p>The document has moved <a href="https://app.example.com/AllowThisUri/Http">here</a>.</p>

如果我完全删除重定向到 HTTPS 逻辑,我确实会得到我期望的前端控制器的响应(它只是打印“hi”进行测试)。

#RewriteCond %{HTTPS} off
#RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
#RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
  #RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

所以我把它全部放回去 - 和我们开始时完全一样,我可以修改R=301如下302

RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]

果然,它改变了糟糕的意外重定向响应:

<title>302 Found</title>
<p>The document has moved <a href="https://app.example.com/index.php">here</a>.</p>

我希望这是合理的。解释起来很冗长,我很抱歉。

我看到的情况是,即使条件!^/AllowThisUri/Http匹配,我们跳过了 HTTPS 重定向规则,规则继续处理,但当到达最后一个将其发送到的规则时index.php,它跳过的规则的重定向部分似乎以某种方式徘徊并影响结果。这对我来说完全没有意义。

重申一下,如果不排除带有 的 URI RewriteCond,非 SSL 到 SSL 重定向将按预期工作。要完全删除所有这些重定向逻辑,index.php前端控制器将使用预期响应进行响应。当我们添加这一个额外的RewriteCondURI 以排除一个 URI 时,它的行为不正确/意外,并返回重定向 TO,index.php而不是对其进行处理并从前端控制器返回预期响应。

也许有人知道一些模糊的设置或者可能是 apache 2.4 的东西会导致这种情况?

答案1

“问题”在于,重写过程在到达最后一个指令时并没有结束目录上下文(.htaccess?)。重写过程重新开始并重复进行,直到 URL 保持不变或发生重定向。

因此,当您提出请求时发生的情况http://app.example.com/AllowThisUri/Http是:

  1. 初始重定向确实被跳过,因为状况明确排除该 URL。
  2. 请求是内部重写按照index.php最后一条规则。

重写过程现在重新开始,使用重写的 URL(即http://app.example.com/index.php):

  1. http://app.example.com/index.php被第一条规则“重定向”到https://app.example.com/index.php。由于该L标志,当前处理轮次停止(即跳过剩余的 2 条重写规则)。

  2. 由于已触发重定向(3xx 响应代码),重写过程不会重新开始,并且会发送重定向响应。

解决方案

一种解决方案是仅在来自客户端的直接请求上触发重定向,而不是重写请求(到index.php)。为此,我们可以向重定向添加一个检查REDIRECT_STATUS环境变量的附加条件。此变量最初为空,在第一次成功重写后设置为“200”。

例如:

# Redirect all app domain urls to ssl
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} "^app\.example\.com$" [NC]
RewriteCond %{REQUEST_URI} "!^/AllowThisUri/Http"
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

(小整理...无需捕捉RewriteRule 图案如果没有使用反向引用。)

如果你只想排除 1 个 URL,那么在RewriteRule 图案,而不是使用条件并检查REQUEST_URI服务器变量(除非这个相同的 URL 也用于其他主机名?)。例如:

# Redirect all app domain urls to ssl
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^app\.example\.com$ [NC]
RewriteRule !^AllowThisUri/Http https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

相关内容