Apache子目录tree中mod_rewrite重写规则不生效问题排查
问题现象
- 根目录
/var/www/html下仅存储.html页面,放置的.htaccess重写规则运行正常 - 子目录
/var/www/html/tree下存储.php文件,放置了和根目录完全一致的.htaccess文件,但重写规则未生效 - 预期实现效果:
- 访问
https://example.com/index.html自动跳转至https://example.com/index - 访问
https://example.com/tree/wallet.php自动跳转至https://example.com/tree/wallet
- 访问
现有配置
.htaccess 文件内容
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^([^/]+)/$ $1.php RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$ RewriteRule (.*)$ /$1/ [R=301,L]
虚拟主机配置(/etc/apache2/sites-available/000-default.conf)
<VirtualHost *:80> <Directory /var/www/html> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> <Directory /var/www/html/tree> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> ServerAdmin webmaster@localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
故障原因
- 目录上下文匹配逻辑错误
.htaccess在子目录生效时,RewriteRule匹配的请求路径会自动移除当前子目录的路径前缀。原有规则写死了/开头的根路径替换格式,子目录下触发跳转时会直接丢失子目录层级,跳转到根目录下的错误路径。 - 规则逻辑和预期不匹配
现有规则仅实现了「访问带尾斜杠的无后缀路径时,内部映射到对应PHP文件」的效果,完全没有编写「访问带.php/.html后缀的路径时,跳转到无后缀地址」的规则,无法实现需求中的跳转效果。 - 缺少重写基准配置
子目录下的.htaccess未配置RewriteBase参数,相对路径格式的重写目标解析会出现偏差,导致文件定位失败。 - 路径匹配规则僵化
原有规则通过正则写死了最多两级路径的匹配格式,无法适配更深层级的目录结构,也无法自动识别根目录的HTML文件和子目录的PHP文件。
修复方案
- 删除
/var/www/html/tree目录下的重复.htaccess文件,统一在根目录/var/www/html/.htaccess中编写全目录通用的重写规则,避免多目录下的路径解析冲突。 - 将根目录下的
.htaccess内容替换为以下通用规则:
RewriteEngine On RewriteBase / # 处理带.php后缀的请求,301跳转到无后缀地址 RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC] RewriteRule ^ /%1 [R=301,L,NE] # 处理带.html后缀的请求,301跳转到无后缀地址 RewriteCond %{THE_REQUEST} \s/+(.+?)\.html[\s?] [NC] RewriteRule ^ /%1 [R=301,L,NE] # 给非文件/非目录的无尾斜杠地址补全尾斜杠 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*[^/])$ /$1/ [R=301,L] # 内部重写:无后缀带尾斜杠路径优先匹配对应PHP文件(适配tree子目录) RewriteCond %{DOCUMENT_ROOT}/$1.php -f RewriteRule ^(.+?)/$ /$1.php [L] # 内部重写:匹配对应HTML文件(适配根目录HTML页面) RewriteCond %{DOCUMENT_ROOT}/$1.html -f RewriteRule ^(.+?)/$ /$1.html [L]
- 执行以下操作使配置生效:
- 执行
a2enmod rewrite确认Apache重写模块已启用,若提示模块已启用直接下一步即可 - 执行
systemctl reload apache2重载Apache配置 - 清除浏览器本地的301跳转缓存(301为永久跳转,本地缓存会导致旧规则持续生效,干扰测试结果)
现有虚拟主机配置中两个目录均已设置AllowOverride All,无需修改。
内容的提问来源于stack exchange,提问作者johnpaperusa
相关产品推荐
相关产品推荐

