最直接有效的方式是用 Route::withoutMiddleware() 显式跳过,它在路由注册阶段切断中间件链,只对指定路由生效,支持类名或别名,仅作用于 middlewareGroups 中的中间件。

想让某个路由不走某中间件,最直接有效的方式是用 Route::withoutMiddleware() 显式跳过,而不是在中间件里做条件判断或改控制器构造函数——后者容易漏掉、难维护,且可能破坏请求生命周期。
用 Route::withoutMiddleware() 绕过指定中间件
这是 Laravel 官方支持、语义最清晰、执行时机最靠前的跳过方式。它在路由注册阶段就切断中间件链,不会触发任何前置逻辑。
- 只对单个路由生效,不影响其他路由:比如
Route::post('/webhook/stripe', [WebhookController::class, 'handle'])->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]); - 可传入完整类名或中间件别名(如
'auth'),多个中间件用数组:->withoutMiddleware(['auth', 'throttle']) - 不能跳过底层全局中间件(
$middleware数组里的),只对$middlewareGroups(如web、api)中定义的中间件有效 - 生产环境必须加注释说明原因,例如
// CSRF 不可用:第三方系统主动回调,无 session/token
在 VerifyCsrfToken 中间件里配置 $except
适用于批量排除 CSRF 验证,但仅限于该中间件本身,且路径匹配逻辑简单——它用 Str::is() 做前缀通配,不支持正则或深层嵌套。
-
$except数组写在app/Http/Middleware/VerifyCsrfToken.php中,例如:protected $except = ['api/*', 'webhook/*', 'pay/notify']; -
api/*匹配/api/users,但不匹配/api/v1/users;如需后者,得写成api/*/*或单独列api/v1/* - 不要写查询参数或完整 URL,只写路径部分;
/health?refresh=1应写为health - API 路由默认不进
web组,所以只要走的是routes/api.php,根本不会经过VerifyCsrfToken—— 这点常被忽略
控制器里用 $this->middleware()->except() 有局限
这个方法只能控制「本控制器内哪些方法跳过中间件」,且只对通过 $this->middleware() 显式挂载的中间件起作用,对路由组自动继承的中间件无效。
- 常见误用:在
HomeController构造函数里写$this->middleware('auth')->except(['index', 'read']);,这能生效;但如果路由本身已属于web组,auth是隐式加载的,这个except()就不起作用 - 它不适用于跳过
VerifyCsrfToken或日志中间件这类全局中间件,因为这些不是你手动挂到控制器上的 - 若控制器方法同时被多个中间件保护(比如
auth和throttle),except()只影响你声明的那个,其余照常执行
中间件 handle() 里提前 return $next($request) 是危险操作
这不是“跳过中间件”,而是“跳过后续所有中间件 + 控制器逻辑”,相当于整个请求被短路。除非你明确要拦截并放行(比如鉴权失败后重定向),否则别这么干。
- 例如在日志中间件里写
if ($request->is('health')) { return $next($request); },看起来跳过了日志,实际也跳过了后续所有中间件(包括StartSession、ShareErrorsFromSession等),可能导致视图渲染异常 - 真正需要跳过日志,应该在中间件开头加
if ($this->shouldSkipLogging($request)) { return $next($request); },但前提是这个中间件本身注册在$middlewareGroups里,且位置合理(比如放在web组靠后) - 更稳妥的做法是:把日志中间件注册在
$middlewareGroups['api']里,而健康检查路由走web组但显式去掉日志中间件



















