我想在 Apache 下的文件mod_rewrite
中进行.htaccess
如下操作:
- 如果请求任何收到文件或路径(例如
foo.txt
或)时,检查子目录中是否存在该文件(例如或 )。如果存在,则显示该文件。foo/bar
public
public/foo.txt
public/foo/bar
- 否则
- 重定向一切至主控制器
index.php
- 重定向一切至主控制器
我尝试的解决方案是这样的:
<IfModule mod_rewrite.c>
RewriteEngine On
# Don't rewrite requests for files in the 'public' directory
RewriteRule ^(public)($|/) - [L]
# For all other files first check if they exist in 'public'
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule ^ public%{REQUEST_URI} [L]
# Let 'index.php' handle everything else
RewriteRule . index.php [L]
</IfModule>
不幸的是,它有一个重大缺陷:它在子目录中不起作用,即当.htaccess
和所有其他文件从 移动/
到 时/sub/
。理想情况下,它可以在随意的当然是子文件夹。
有人能帮忙吗?我该如何修复.htaccess
才能消除这个缺陷?
答案1
我遇到过类似的问题,我想分发一个行为如您描述的 Web 应用程序:特定目录中有一堆静态文件,可以像访问 Web 应用程序根目录一样访问它们(即尝试/images/pic.png
获取其中的图像public/images/pic.png
),其他所有内容都映射到控制器脚本。
我遇到的第一个问题是-f
在RewriteCond
不适用于相对路径 - 即,如果“TestPattern”字符串可以解析为相对于指定条件的目录的文件(使用.htaccess
),-f
仍然会返回“不匹配” (1)。
因此,我们需要尝试“理解”文件所在的每个目录前缀是什么.htaccess
,并mod_rewrite
实际上为我们解决这个问题,并确保RewriteRule
始终相对于每个目录前缀.htaccess
文件进行评估 - 但不幸的是,它不会以任何可以访问的方式公开该信息RewriteCond
(他们真的应该让我们拥有它)。
话虽这么说,显然你可以做一些奇怪的事情来RewriteCond
强制“CondPattern”梳理出%{REQUEST_URI}
和RewriteRule
输入之间的关系——阅读这个问题的答案实现所有奇怪的目标。
但是,将这些技巧简化为手头的问题(匹配-f
相对于 .htaccess 的正确路径,无论它在哪里),我们得到这个相当简单的事情:
RewriteEngine On
RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2
RewriteCond %{DOCUMENT_ROOT}%1static/%2 -f
RewriteRule ^(.*)$ static/$1 [END]
RewriteRule . index.php [END,QSA]
解释:
中的正则表达式RewriteCond
非常有限,因为它无法解析变量和捕获的文本,但它可以使用语法来反向引用自身\<number>
。因此,我们首先创建一个由以下内容组成的字符串:
- 原始请求 URI
- 我们不希望在 URL 中出现的硬编码分隔符(该
::
部分)。 RewriteRule
将匹配的URI 的本地(每个目录)部分- 这是$1
部分:RewriteCond
可以查看目标的捕获组RewriteRule
(2),因此我们捕获整个输入。
然后,我们应用一个正则表达式来解释该请求 URI 和输入之间的关系RewriteRule
- 请求 URI 由两部分组成(第一次和第二次捕获),其中第一部分以 结尾,/
第二部分与::
分隔符后出现的内容相同 -\2
只是在同一个正则表达式中引用第二次捕获,并表示它必须相同。
结果是,我们将请求 URI 分解为两个捕获组 - 第一个是mod_rewrite
私下享有的本地每个目录前缀,第二个是我们要检查的该目录下的本地路径。第二个条件用于%<number>
引用这些捕获,在文档根目录下构建绝对路径(3)。请注意,%1
捕获前缀 URI 的前斜杠和尾斜杠。
最后,请注意,我使用的是标志END
而不是L
(last),因为它更快并且mod_rewrite
立即存在。这样,我就可以删除带有存在的行RewriteRule … - [L]
,只是为了停止行为,而L
这实际上不会停止处理:它只是返回开头或使用新 URL 重写规则。END
要简单得多。
脚注:
- 实际上,相对路径解析在某些条件下确实有效,如该问题的错误报告。它只是不能按预期工作,并且在大多数常见配置中根本不起作用。
- 这种看上去很奇怪的向后行为其实很简单:Apache 总是先解析
RewriteRule
,然后才检查条件是否允许应用它。 Alias
顺便说一句,如果您使用或类似方法将服务器 URI 空间的部分映射到文档根目录下的其他位置,则此方法将不起作用。请参阅文档以CONTEXT_DOCUMENT_ROOT
了解如何解决此问题。
答案2
您似乎不想访问 /public 文件夹之外的任何内容?如果是这样,这对我来说似乎很容易,而且不需要任何重写(我希望您实际上没有任何理由想使用mod_rewrite
!
- 将index.php放在公共文件夹中
- 将 DocumentRoot 设置为公共文件夹
- 使用 ErrorDocument 将任何未找到的文件重定向到 index.php
如果您有理由使用公共文件夹,那么您可以使用mod_rewrite
将除 index.php 之外的任何内容重定向到 public/anything,并且仍然使用 ErrorDocument 重定向到 index.php。
答案3
这套规则对我有用。我省略了对以 /public 开头的路径的检查。
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule ^/(.*) /public/$1 [L]
RewriteRule . /index.html [L]
如果/public
不在您的文档根目录中,您可以将 %{DOCUMENT_ROOT} 替换为 /public 目录的磁盘路径。 可能CONTEXT_DOCUMENT_ROOT
包含文件内容目录的文件路径,除非 中的文件与 中的.htaccess
文件相同,否则不会匹配。我通过使用我的等效项作为我的文档根目录来避免整个问题。如果您无法输入,您可以使用来访问它所在的位置。/public/public
/public
RewriteCond
/public
index.php
/public
Alias
您的方法涉及重写所有匹配项。用作文档根目录的备用规则集../public
将是:
别名 /index.php /var/www/index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]
您还可以使用自定义 404 错误页面处理丢失的文件。
答案4
RewriteBase
将其移动到子文件夹时尝试使用:
RewriteBase /subfolder/
# Rules