中间件执行顺序由注册位置决定:全局中间件最先注册最晚执行(栈式),路由组中间件居中,路由级中间件最后执行;调用 next($request) 的位置决定放行时机,返回值流向影响后续处理。

中间件注册位置决定执行顺序
中间件的执行顺序不是靠「优先级数字」或配置项控制,而是严格由注册位置决定:全局中间件最先注册的最晚执行(类似栈),路由组中间件在全局之后、路由中间件之前,而单个路由上用 middleware() 指定的中间件排在最后(即最靠近业务逻辑)。
常见错误是以为在 app/Http/Kernel.php 里把中间件写在前面就「优先执行」——实际相反:它会最后被调用(因为是「进入时最先压栈,退出时最后弹出」)。
- 全局中间件(
$middleware数组):所有请求都走,但执行时机最靠外(比如日志、加密响应) - 分组中间件(
$middlewareGroups,如web或api):只在对应路由组中生效,包裹在全局中间件内层 - 路由级中间件(
Route::middleware([...])):最内层,离控制器最近,适合做权限校验、参数预处理
如何快速验证当前中间件执行顺序
最直接的办法是在每个中间件的 handle() 方法开头加一行日志或 dd(),观察输出顺序。别依赖文档猜测,Laravel 的中间件堆叠是确定性的,但嵌套层级容易看错。
注意:如果用了 Route::group(['middleware' => [...]]) 和单个路由的 ->middleware([...]) 同时存在,后者会追加到前者之后,而不是覆盖。
-
dd('before controller', $request->path());放在中间件handle()开头 - 不要在
terminate()里调试顺序,它和请求流程是分离的 - API 路由默认走
api中间件组,和web组完全不重叠,别混着查
中间件里调用 next($request) 的位置影响执行流
中间件是否「放行」以及何时放行,取决于你在哪里调用 next($request)。它不是必须放在最后——你可以前置判断后直接返回响应,也可以在中间插入逻辑再放行,甚至不调用它来中断流程。
典型陷阱:在中间件里做了耗时操作(比如 DB 查询),又把它放在 $middleware 全局数组里,结果所有请求(包括静态资源)都被拖慢。
- 想提前终止?直接
return response(...),别调用next() - 想在控制器之后再处理(比如修改响应体)?确保
next($request)是handle()的最后一个表达式,并在其返回值上操作 - 多个中间件串联时,前一个的返回值就是后一个的输入,所以返回非
Response实例会导致报错UnexpectedValueException: The response is not a Response instance.
Laravel 9+ 的「前置中间件」和「后置中间件」本质没变
所谓「前置」只是语义描述:指在控制器执行前起作用的中间件;「后置」是指依赖控制器已返回响应才能工作的中间件(比如修改 Header、压缩内容)。但底层仍是同一个 handle() 流程,没有独立的钩子或新生命周期。
真正区分前后的是你对 next($request) 的调用时机和对返回值的处理方式,不是注册位置或命名规范。
- 不要给中间件起名带
Before/After就以为框架会自动调度 - 想确保某中间件一定在响应生成后运行?必须等
$response = $next($request)执行完,再操作$response - 使用
terminate()方法的中间件不会影响主请求流,它在响应已发送后异步触发,不适合做关键逻辑
中间件顺序本身不复杂,难的是理清「注册位置 → 堆叠层级 → 调用时机 → 返回值流向」这四层关系。一个中间件在 $middlewareGroups['web'] 里,又被手动加到某个路由上,那它其实会执行两次——这点最容易被忽略。











