mod_rewrite RewriteRule 中的相对替换

mod_rewrite RewriteRule 中的相对替换

我想创建一个 mod_rewrite重写规则这与网页的安装位置无关。我想在 .htaccess 文件中定义重写规则。让我们以此为例:

RewriteEngine on
RewriteRule ^(.*)\.html html.php

使用此规则,我想将所有 *.html 请求映射到位于 Web 根目录中的 html.php 脚本。问题是,Web 根目录的公共基本 URL 可能会发生变化。因此,Web 根目录可能位于http://www.somewhere.tld/或 的某个子目录中http://www.somewhere.tld/foo/bar/

但在重写规则中使用相对路径不起作用。所以我必须编写以下代码之一:

/html.php (When web is located in root directory of the web)
/foo/bar/html.php (When web is located in foo/bar sub directory)

或者,我可以设置 RewriteBase,但我根本不想配置此路径。我希望 apache 自动执行正确的操作,这样我就可以将 Web 复制到某个目录,它就可以正常工作,而无需告诉重写规则 Web 位于何处。我该怎么做?

答案1

据我所知,您无法实现这一点。您必须配置 RewriteBase。一种方法是使用 PHP 脚本自动设置 RewriteBase,也许?但这至少需要 .htaccess 上的写入权限。但您必须在 .htaccess 中配置 RewriteBase。

答案2

我也曾遇到过同样的问题,原因也一样。我试图让 Web 应用独立于其安装位置,而无需借助配置脚本或用户手动干预。只需将应用放在某个地方,让它自行运行即可。

看来至少对于 Apache 2 来说还是有解决方案的。只需四行代码。不过,解释其背后的想法需要不止四行代码 ;)

Tl;dr 尝试这个:

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$
RewriteRule ^(.*)$ %2index.php [QSA,L]

原因和方法如下

  • 我们不能动态地设置 RewriteBase,所以我们一次性将它设置为服务器的根 URL:RewriteBase /。这提供了一致性,但这也意味着我们必须自己建立当前目录的 url 路径并将其作为重写的 URL 的前缀。

  • 那么我们在哪个目录中?假设REQUEST_URI/some/path/app-root/virtual/stuff。我们的 htaccess 在 app-root 中。如果我们抓住虚拟部分 - virtual/stuff - 并将其从 中删除REQUEST_URI,我们就剩下我们的 app 目录的 url 路径。

  • 捕获虚拟部分很简单,可以在重写规则本身中进行。RewriteRule ^(.*)$ ...使其在$1变量中可用。

  • 现在我们执行小字符串操作并从请求 URI 中删除虚拟部分。我们没有用于此的字符串命令,但 RewriteCond 可以匹配字符串并捕获子字符串。因此,我们将添加一个 RewriteCond,其唯一目的是提取当前目录的 url 路径。除此之外,“条件”应该不会妨碍我们,并且始终为真。

  • 我们可以$1在 RewriteCond 中使用 RewriteRule 中的变量,因为 mod_rewrite 实际上是反向处理规则集。它从规则本身中的模式开始,如果匹配,则继续检查条件。因此,此时变量可用。

  • 虽然 RewriteCond 中的测试字符串可以使用变量,但实际条件正则表达式不能。在条件内部,我们只能使用内部反向引用。因此,首先,我们组装一个测试字符串“[虚拟部分][某些分隔符][请求 uri]”。'#' 字符是一个很好的分隔符,因为它不会显示在 URL 中。接下来,我们将其与条件进行匹配

    ([^#]*) - anything up to the separator, captures the virtual part
    #       - the separator
    (.*?)   - anything in the request uri up to what we've captured in group one,
              grabs the current directory url-path
    \1$     - group one again, ie the virtual part of the request uri
    

    完整的条件如下:RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$

  • RewriteCond 正则表达式中的第二个捕获组是我们的位置。我们只需在重写的 URL 前面加上一个%2引用即可。这样就得到了RewriteRule ^(.*)$ %2index.php [QSA,L]。好了。

我还没有进行过广泛的测试,但我已经确定它可以与普通虚拟主机以及大规模虚拟主机(使用 VirtualDocumentRoot)配合使用。这意味着其他别名位置也应该没问题。

Apache 1.3

不幸的是,Apache 1.3 仍然存在,并且它将受 RewriteCond 模式的阻碍。Apache 1.3 不支持非贪婪修饰符(中的“?” (.*?))。

但对于 Apache 2,它应该可以解决问题。不过,我非常欢迎任何反馈,特别是如果它在您的环境中失败了。

编辑:我刚刚在我的网站上发布了一篇关于这个主题的更全面的文章博客(“在 .htaccess 文件中使用 mod_rewrite 而不了解 RewriteBase“)。请参阅此处了解更多详细信息。

答案3

我发现Apache 的文档误导:

在 .htaccess 文件中使用重写引擎时每个目录的前缀(对于特定目录来说总是相同的)会被自动删除对于 RewriteRule 模式匹配和自动添加任何相对(不以斜杠或协议名称开头)替换遇到规则集结尾后。有关将添加回相对替换的前缀的更多信息,请参阅 RewriteBase 指令。

但它添加回来的前缀完全不同(磁盘上的路径而不是原始 URL)。我想不出在什么情况下这是正确的行为。

答案4

比如

RewriteEngine on
RewriteRule ^(.*)\.html $1/html.php

不过,我的示例保留了 html 文件的文件名部分——例如,page1.html 将被重定向到 page1/html.php。(注意:我根本没有测试过这一点,请自行承担风险:))

还,mod_rewrite 指南有大量与您的问题类似的示例。您已经看过了吗?

相关内容