返回

Apache mod_alias与mod_rewrite共存配置详解

windows

让 mod_rewrite 和 mod_alias 和谐共处

我碰到了一个棘手的问题:在 Windows、Apache 和 PHP 环境下,mod_aliasmod_rewrite 两个模块没法一起好好工作。明明服务器和站点层面都启用了这两个模块,可就是不行。

我的 DocumentRoot 设置在 d:\web\public_html\,不过这文件夹一般是空的。

我的网站文件都放在 d:\web\sites\ 里面,目录结构是这样的:

d:\web\sites\alfa
d:\web\sites\alfa\public_html
d:\web\sites\bravo
d:\web\sites\bravo\public_html

我用了 Alias 指令来设置虚拟路径(这个设置没问题,可以正常访问):

Alias /alfa d:/web/sites/alfa/public_html

<Directory "D:\web\sites\alfa\public_html">
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

每个站点的 .htaccess 文件都在对应的 public_html 文件夹里。我确定启用了 mod_rewrite,但它就是不好使。

现在卡在 RewriteBase 的设置上。我试了 //alfa/alfa/alfa/,甚至还试了绝对路径 d:\web\sites\alfa\public_htmld:/web/sites\alfa/public_htmld:\web\sites\alfad:/web/sites/alfa,都没用!

这俩模块到底能不能一起用?如果可以,我错在哪儿了?

我当然希望 Alias 指令集中管理,RewriteRule 写在每个站点的 .htaccess 文件里。

问题根源

这问题多半出在 Apache 处理请求的流程上,以及 mod_aliasmod_rewrite 两个模块处理 URL 的时机和方式不同。

  1. 处理顺序: mod_aliasmod_rewrite 之前处理 URL。Alias 指令直接把 URL 映射到文件系统路径,而 mod_rewrite 是在 URL 已经被初步映射之后,再进行重写。

  2. 路径上下文: .htaccess 文件中的 mod_rewrite 指令是在特定目录的上下文中执行的。Alias 创建的虚拟路径改变了这个上下文,导致 RewriteBase 设置不当,重写规则无法正确匹配。

  3. Per-directory vs. Per-server 配置: Alias 属于服务器配置级别。.htaccess 是 per-directory 配置, 如果直接在httpd.conf 这类主配置里设置了 Alias, 那么它会影响后续所有 per-directory (比如 .htaccess) 配置.

解决方案

解决问题的关键在于理解 Apache 处理请求的流程,以及如何协调 mod_aliasmod_rewrite 的工作。以下是几种可行的方案:

方案一:调整 RewriteBase

这是最直接的尝试,就是找对 RewriteBase 的值。 既然 Alias/alfa 映射到了 d:/web/sites/alfa/public_html,那 .htaccess 文件中的 RewriteBase 就应该相对于这个映射后的路径。

  1. 原理: RewriteBase 指定了重写规则中相对 URL 的基准路径。由于 Alias 已经改变了 URL 的映射,RewriteBase 应该反映这个变化。

  2. 操作:d:\web\sites\alfa\public_html\.htaccess 文件中,设置:

    RewriteEngine On
    RewriteBase /alfa/
    
    # 其他重写规则
    RewriteRule ^test\.html$  another.html
    
    • 上面示例的意思是 如果请求是 /alfa/test.html, 那么重定向到 /alfa/another.html
    • 这个设置假设你的重写规则都是针对 /alfa/ 下的路径的。如果还有更深层次的路径,可以相应地修改 RewriteRule。
  3. 安全建议: 如果.htaccess可以被外部访问,需要注意避免通过重写规则暴露服务器内部文件。

方案二: 使用 RewriteRule 中的绝对 URL

另一种方法是在 RewriteRule 中直接使用绝对 URL,避开 RewriteBase

  1. 原理: 通过在 RewriteRule 的替换部分使用完整的 URL(包括 http://https://),可以绕过对 RewriteBase 的依赖,确保重写规则在任何上下文中都能正确工作。

  2. 操作:d:\web\sites\alfa\public_html\.htaccess 文件中,可以这样写:

    RewriteEngine On
    RewriteRule ^test\.html$ /alfa/another.html [L]
    
    

    或者使用:

    RewriteEngine On
     RewriteCond %{REQUEST_URI} ^/alfa/
    RewriteRule ^test\.html$ /alfa/another.html [L]
    
    

    注意这里没有 RewriteBase。第一个例子中, 第二个参数 /alfa/another.html 被认为是相对于 DocumentRoot. 第二个例子加入了条件判断, 确保只对 /alfa/ 开头的请求使用这条规则.

  3. 安全建议: 避免在重写规则中硬编码域名,如果将来域名变更,需要修改所有规则。

方案三:将 Alias 移入

如果可能,尽量避免直接在主配置文件中使用 Alias,而是在 <VirtualHost> 块中配置。

  1. 原理: <VirtualHost> 为每个站点提供了一个独立的配置环境,更清晰,也更容易控制。把 Alias 放在 <VirtualHost> 里,可以减少它对全局配置的影响,避免与 .htaccess 中的 mod_rewrite 规则冲突。

  2. 操作:

    • 在 Apache 配置文件中 (httpd.conf 或者 conf.d 中的独立文件):
    <VirtualHost *:80>
        ServerName alfa.local
        DocumentRoot d:/web/sites/alfa/public_html
    
        Alias /alfa d:/web/sites/alfa/public_html
    
          <Directory "D:\web\sites\alfa\public_html">
              Options Indexes FollowSymLinks
              AllowOverride All
              Order allow,deny
              Allow from all
            Require all granted
          </Directory>
    </VirtualHost>
    
    • 然后, 在 d:\web\sites\alfa\public_html\.htaccess 文件里, 你就可以设置:
     RewriteEngine On
     RewriteBase /
    RewriteRule ^test\.html$ another.html [L]
    
    
  3. 额外说明: 你需要在你的 hosts 文件 (Windows 上一般在 C:\Windows\System32\drivers\etc\hosts) 加入:

    127.0.0.1  alfa.local
    
  4. 安全建议: 这种方式能更好的控制每个站点的配置, 更推荐使用。

方案四: 使用 [PT] 标志 (较复杂, 进阶技巧)

如果上述方法还不能解决问题,可能需要用到 [PT] (passthrough) 标志。这个标志会强制 Apache 重新处理重写后的 URL,让 mod_alias 有机会再次介入。

  1. 原理: mod_rewrite 默认只处理一次 URL。[PT] 标志会把重写后的 URL 再次交给 Apache 的 URL 处理流程,包括 mod_alias

  2. 操作:d:\web\sites\alfa\public_html\.htaccess 中:

    RewriteEngine On
    RewriteBase /alfa/
    
    RewriteRule ^test\.html$ another.html [PT]
    

    或者结合 RewriteCond:

      RewriteEngine On
     RewriteCond %{REQUEST_URI} !^/alfa/
    RewriteRule ^(.*)$ /alfa/$1 [PT]
    
    

    第一个例子的含义是: 请求 /alfa/test.html, 会先被 rewrite, 然后, [PT]标志使请求又经过一次完整的处理流程.

    第二个例子稍微进阶, 可以用来强制把非 /alfa/ 开头的 URL 全部转为以 /alfa/ 开头. 比如请求 /images/a.jpg, 会被改为 /alfa/images/a.jpg, 然后 [PT] 标志会触发内部重定向。

  3. 注意事项: 使用[PT]要小心,因为它可能导致无限循环重写。 确保你的规则不会产生循环。同时,[PT] 的效果依赖于服务器的整体配置,可能不是在所有情况下都有效。

选择哪个方案?

  • 如果只是简单的重写,优先尝试方案一 (调整 RewriteBase)。
  • 如果想避免 RewriteBase 的麻烦,可以使用方案二 (绝对 URL)。
  • 如果追求更清晰、更规范的配置,方案三<VirtualHost>)是最好的。
  • 方案四 适合一些极端和进阶情况, 通常不太建议使用。

具体选择哪个方案,取决于你的具体需求和对配置复杂度的接受程度。建议逐个尝试,找到最适合你的方案。

希望上面的方法能帮你搞定!