使用.htaccess将通配符子目录重写到单一子目录
多租户项目的.htaccess重写规则修复方案
需求说明
需要搭建多租户项目,URL结构如下:
https://example.com/TENANTNAME/file.phphttps://example.com/TENANTNAME/api/file.phphttps://example.com/TENANTNAME/assets/js/main.js
要求将所有请求内部重写到单一子目录/BASE/,只维护一套核心文件,通过URL中的TENANTNAME识别租户。
原规则问题
尝试了以下.htaccess规则,仅根目录的index.php能正常工作,assets等深层子目录下的文件会触发服务器错误:
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !^/BASE/(.*)/ RewriteRule ^(.*)/(.*)$ /BASE/$2 [L]
预期重写效果:
https://example.com/TENANTNAME/index.php→ 内部指向https://example.com/BASE/index.phphttps://example.com/TENANTNAME/assets/js/main.js→ 内部指向https://example.com/BASE/assets/js/main.js
问题分析
原规则存在两个核心问题:
^(.*)/(.*)$的贪婪匹配会错误截断路径:当URL包含多层子目录时,第一个.*会匹配到TENANTNAME/assets/js这类长字符串,导致重写后路径丢失中间层级,变成/BASE/main.js而非预期的完整路径RewriteCond %{REQUEST_URI} !^/BASE/(.*)/的正则写法冗余且有误,末尾的斜杠会导致无法正确匹配/BASE/下的文件,反而干扰重写逻辑
修复后的规则
RewriteEngine On # 排除BASE目录本身的请求,避免重写循环 RewriteRule ^BASE/ - [L] # 匹配开头的租户名(不含斜杠),将后续完整路径重写到BASE目录 RewriteRule ^([^/]+)/(.*)$ /BASE/$2 [L]
规则解释
RewriteRule ^BASE/ - [L]:直接放行/BASE/目录下的请求,防止出现重写循环^([^/]+)/(.*)$:精准捕获第一个路径段作为租户名([^/]+确保只匹配不含斜杠的字符串),后续所有路径完整保留,重写到/BASE/目录的对应位置
修复后,无论请求是根目录文件还是深层子目录资源,都能正确映射到/BASE/下的对应文件。你可以通过解析原URL或读取$_SERVER['REQUEST_URI']来获取租户名TENANTNAME。
内容的提问来源于stack exchange,提问作者user3752011
相关产品推荐
相关产品推荐

