Laravel 12 中 CSRF 中间件默认启用且已注入 web 组,无需手动注册;419 错误主因是表单缺失 @csrf 或路由未走 web 组;API 路由默认不校验 CSRF,需手动配置;豁免路径须精准匹配请求路径,避免过度宽松。

CSRF中间件默认就开着,别手动加
在 Laravel 12(及当前主流版本)中,VerifyCsrfToken 中间件已默认启用,且被自动注入到 web 中间件组里。你不需要在 bootstrap/app.php 或 app/Http/Kernel.php 里额外注册它——只要路由走的是 web 组(比如定义在 routes/web.php 中),CSRF 验证就已生效。
常见错误现象:419 Page Expired 错误反复出现,但你翻遍中间件配置也没找到问题——其实不是没开,而是表单漏了令牌,或请求发到了不该走 web 的地方。
- 检查你的路由是否真在
web组:比如api.php下的路由默认不走 CSRF,哪怕你写了 POST 表单也会被拒绝 -
php artisan route:list看一眼中间件列,确认目标路由是否带web - 不要在
Kernel.php的$middlewareGroups['web']里重复添加VerifyCsrfToken::class,Laravel 会报错或静默忽略
POST 表单必须带 @csrf,否则 419 是必然结果
Blade 模板里所有非 GET 表单(POST、PUT、PATCH、DELETE)都得显式插入 @csrf。它不是可选装饰,是硬性校验字段;没它,Laravel 直接拦截,连控制器都不会进。
常见错误现象:前端用 JS 拼 HTML 表单、或用 v-html 渲染服务端返回的表单片段,@csrf 被当成纯文本没执行,导致提交必 419。
-
@csrf会生成一个隐藏字段:<input type="hidden" name="_token" value="xxx"> - 不能用
{{ csrf_token() }}手动写——它只输出值,不生成字段;也不要用old('_token')替代,那是回填用的,不是 CSRF 校验源 - 如果表单是动态加载的(如 AJAX 加载弹窗表单),需确保该片段由 Blade 渲染,或在 JS 中提前取好
csrf_token()值并手动注入
API 路由想用 CSRF?得手动塞进 web 组或改中间件组
默认情况下,api.php 路由走的是 api 中间件组,而这个组里没有 VerifyCsrfToken——这是设计使然,因为 API 通常走 Token 认证(如 Sanctum/Passport),不依赖 Cookie 会话。
但如果你的 SPA 是同域部署、用 Session 登录、又想复用 CSRF 防护(比如避免引入额外认证机制),就得主动启用:
- 方法一(推荐):把路由挪到
web.php,并加前缀如/api/v1,再用Route::middleware('web')->group(...)包裹 - 方法二:修改
app/Http/Kernel.php的$middlewareGroups['api'],在数组里加上\App\Http\Middleware\VerifyCsrfToken::class - 注意:加完后,所有
api路由都会校验 Cookie +X-CSRF-TOKEN或_token,但第三方工具(如 Apipost、curl)发请求时若没带会话 Cookie,仍会失败——这不是 bug,是机制本身要求“有状态”
豁免特定路由要谨慎,别把 webhook 或回调全放行
需要跳过 CSRF 校验时(比如支付回调、第三方推送),应在 app/Http/Middleware/VerifyCsrfToken.php 的 $except 数组里声明路径。但这里容易犯两个错:
- 写死完整 URL(如
'https://example.com/webhook')——Laravel 只匹配请求路径($request->path()),协议和域名无效 - 用过于宽泛的通配符(如
'*'或'api/*')——可能意外放过敏感接口,比如api/user/delete - 正确写法是基于路径前缀+斜杠结尾:
'webhook/stripe'、'payment/callback/*',注意末尾/*才表示子路径通配
真正难搞的点不在怎么加豁免,而在“谁该被豁免”——比如 Stripe webhook 必须豁免,但它的签名验证逻辑必须严格,否则豁免就等于开门揖盗。


















