ThinkPHP路由强制模式需同时满足url_route_on=>true、url_route_must=>true、清空runtime/route/缓存、注册miss路由,且需拦截query参数及独立配置子域名/多应用路由。

ThinkPHP 的 URL 路由模式不是“选一个开关”就能生效的,必须同时满足配置项、缓存清理、入口行为三者协同,否则哪怕写了 url_route_must => true 也形同虚设。
确认当前路由模式类型及对应配置项
ThinkPHP(5.x / 6.x)实际运行的路由模式由两个关键配置共同决定:url_route_on 和 url_route_must。它们不是互斥选项,而是分层控制:
-
url_route_on => false:整个路由系统关闭,所有请求走默认m/c/a解析(不推荐) -
url_route_on => true且url_route_must => false:启用「混合模式」——先匹配 route.php 规则,不命中则 fallback 到index.php?m=xxx&c=yyy&a=zzz(默认行为) -
url_route_on => true且url_route_must => true:启用「强制模式」——仅响应 route.php 中明确定义的规则,其余一律 404
注意:url_route_must 在 ThinkPHP 6 中已移入 config/app.php 的 route 子数组;5.x 则在 config/route.php 顶层。
为什么改了配置却没生效?清缓存只是第一步
ThinkPHP 会把路由规则编译后写入 runtime/route/ 目录。只要这个目录存在,框架就跳过重新解析 route.php,直接加载缓存文件——所以你改的配置根本没被读进去。
立即学习“PHP免费学习笔记(深入)”;
- 必须彻底删除
runtime/route/整个文件夹(不是清空内容) - 确保 Web 服务器对
runtime/有写权限,否则下次请求无法重建缓存 - 若使用 CLI 命令(如
php think route:clear),需确认该命令是否真能删掉route/目录(部分版本只清内容)
验证方式:删完后访问任意未定义路径,看是否返回 404 而非控制器执行结果。
强制模式下仍能通过 ?m=xxx 访问?得加 input 校验拦截
url_route_must => true 只管 PATH_INFO 部分(即 /user/profile 这段),对 query string 中的 m、c、a 参数完全不拦截。攻击者仍可构造 index.php/user/profile?m=admin&c=Index&a=delete 绕过路由。
- 在
app/common.php或app/event.php中注册app_init事件监听器 - 插入如下校验逻辑(ThinkPHP 6 写法):
if (input('m') || input('c') || input('a')) { throw new \think\exception\HttpException(404); } - 务必放在路由匹配前执行,否则拦截无效
子域名和多应用路由必须独立注册
启用 url_route_must 后,主域名与子域名之间、不同应用之间(如 admin 和 api)的路由规则不再共享或继承。
- 子域名需开启
url_domain_deploy => true,并在route.php中用Route::domain('api')->group(...)显式包裹 - 多应用模式下,每个应用的路由必须单独写在
app/{name}/route/route.php,不能指望全局route.php覆盖 - 资源路由(
Route::resource())、闭包路由等动态规则,一旦定义就必须被显式访问,否则仍属“未定义”,触发 404
最常被忽略的一点:强制模式下,miss 路由(Route::miss())不是兜底逻辑,而是唯一合法的“未命中处理入口”——它必须存在,且不能抛出异常以外的响应。



















