如何阻止Apache加载与URL名称相似的文件?附.htaccess及虚拟主机配置异常问题
问题背景
我配置了如下.htaccess和虚拟主机文件,但访问https://www.some-domain.net/login/generic_oauth时,Apache错误加载了已存在的login.php文件;将login.php重命名为loggin.php后,该异常问题消失。
我的.htaccess配置:
RewriteEngine On RewriteBase / RewriteCond %{REQUESTFILENAME} !-f RewriteCond %{REQUESTFILENAME} !-d RewriteRule ^login/generic_oauth$ auth0-callback.php [L] RewriteRule ^(.*)$ index.php?page=$1 [L]
我的虚拟主机配置:
<VirtualHost *:443> DocumentRoot /home/blabla/www/frontend/ ServerName some-domain.net ServerAlias www.some-domain.net SSLEngine on SSLCertificateFile /etc/apache2/crt/cert.pem SSLCertificateKeyFile /etc/apache2/crt/key.pem <Directory /> Options -Indexes +FollowSymLinks -MultiViews AllowOverride All Require all granted </Directory> <Directory /home/blabla/www/> Options -Indexes +FollowSymLinks -MultiViews AllowOverride All Require all granted </Directory> </VirtualHost>
问题根源
这个坑我之前也踩过!核心原因是Apache的MultiViews内容协商机制在搞鬼:当MultiViews启用时,Apache会自动匹配请求路径前缀对应的文件。比如你请求/login/generic_oauth,它会扫描目录里有没有login.*格式的文件(刚好你的login.php存在),然后直接返回这个文件,完全跳过了你的Rewrite规则。
虽然你在vhost里写了-MultiViews,但可能全局Apache配置里还开着这个选项,或者.htaccess没有覆盖到位。另外,你的Rewrite规则还有个小问题:RewriteCond只对紧跟的第一个RewriteRule生效,第二个规则会无条件执行,不管请求的文件/目录是否存在,这也可能埋下隐患。
解决方案
1. 在.htaccess里明确禁用MultiViews
直接在.htaccess开头加上Options -MultiViews,确保这个机制被彻底关掉:
Options -MultiViews RewriteEngine On RewriteBase /
2. 修正Rewrite规则的条件范围
把文件/目录存在性检查的条件应用到所有RewriteRule,避免不必要的重写:
Options -MultiViews RewriteEngine On RewriteBase / # 处理login/generic_oauth的规则,仅当文件/目录不存在时触发 RewriteCond %{REQUESTFILENAME} !-f RewriteCond %{REQUESTFILENAME} !-d RewriteRule ^login/generic_oauth$ auth0-callback.php [L] # 处理其他请求的规则,同样只在文件/目录不存在时触发 RewriteCond %{REQUESTFILENAME} !-f RewriteCond %{REQUESTFILENAME} !-d RewriteRule ^(.*)$ index.php?page=$1 [L]
3. 重启Apache生效
修改完配置后,重启Apache服务让变更生效:
sudo systemctl restart apache2
这样调整后,Apache会优先执行你的Rewrite规则,不会再去匹配login.php,同时也保证了只有当请求的资源不存在时才会走重写逻辑。
内容的提问来源于stack exchange,提问作者piterek

