从 ThinkPHP 5.1 迁移到 ThinkPHP 8.0 时,不少开发者会在路由配置上遇到棘手问题:看似正确的路径却返回 404,反复排查后发现,根源往往在于路径大小写的处理差异。
在 TP5.1 中,底层通过 strtolower 自动将路径统一,因此大小写不敏感。但 TP8.0 的设计策略发生了变化:路由路径匹配默认严格区分大小写。这并非 Bug 或遗漏,而是架构层面的调整。如果你遇到 /Index/Index 返回 404,而 /index/index 正常,那么问题就出在这里。
TP8.0 的路由路径默认区分大小写。可通过单条规则添加option(['case_sensitive'=>false])或全局调用Route::setDomainCase(false)来关闭区分。推荐使用 NormalizePath 中间件将所有路径统一转小写并重写 PATH_INFO,从而确保全路由匹配一致。

路由路径大小写敏感性的配置方法
TP8.0 提供了两种粒度的开关,但需要明确:它们仅作用于 URL 路径段(例如 /hello/world 中的 hello 和 world),并不会影响控制器类名或方法名。这一点常被误解。
单条规则关闭大小写敏感:
Route::get('hello/:name', 'index/hello')->option(['case_sensitive' => false]);全局关闭(TP6.1+ 及 TP8.0 支持),在
app/route/app.php开头调用:Route::setDomainCase(false);
⚠️ 需要警惕:case_sensitive => false 并不会让 /INDEX/HELLO 自动映射到 IndexController::hello()。PHP 类加载规则依然适用——类名首字母大写(IndexController),方法名小写(hello)。路径大小写忽略 ≠ 类名/方法名自动转换。
更推荐的解决方案:使用统一转小写中间件
依赖 case_sensitive 逐个配置容易遗漏,尤其在多模块、多路由文件的项目中,逐一设置非常繁琐。有没有更彻底的解决办法?
推荐在请求入口做标准化处理,新建中间件 app/middleware/NormalizePath.php:
path();
if ($path && !str_starts_with($path, '/')) {
$path = '/' . ltrim($path, '/');
}
$lowerPath = strtolower($path);
// 重写 PATH_INFO,确保后续路由解析基于小写路径
$_SERVER['PATH_INFO'] = $lowerPath;
$request->path($lowerPath);
return $next($request);
}
}
然后在 app/middleware.php 中将其置于最前:
return [
app\middleware\NormalizePath::class,
// 其他中间件...
];
这种做法的优势明显:所有路由(包括注解路由、子应用路由、多语言路由)均基于小写路径匹配,无需每条规则重复配置 case_sensitive,实现一劳永逸的效果。
多应用场景下的注意事项
如果使用了 topthink/think-multi-app,每个子应用(如 app/admin/)的路由也要遵循相同逻辑。Route::import('admin', 'admin') 导入的路由同样受 case_sensitive 控制。而中间件方式天然覆盖全部子应用,更加省心。
Web 服务器层补充(Linux 生产环境必查)
另外需要留意:Nginx/Apache 对路径本身是区分大小写的。这意味着 /Index/Hello 和 /index/hello 实际是两个不同的路径,即使框架开启了 case_sensitive => false,Web 服务器也可能在转发给 PHP 之前直接返回 404。
因此,建议检查 Nginx 配置中的 location / 块,确保它正确地将所有非静态资源转发到 index.php,并且 fastcgi_split_path_info 已经启用。否则 $_SERVER['PATH_INFO'] 为空,中间件即使生效也无用武之地。
