419 page expired 错误并非 csrf 未开启,而是请求缺失或携带了无效 _token;根本原因在于前端漏传(表单缺 @csrf)、ajax 未正确设置 x-csrf-token 请求头,或 session 未正常存储/同步。

419 Page Expired 不是 CSRF 没开,而是请求根本没带有效 _token —— 问题几乎总出在前端漏传、AJAX 头没设对、或 session 没存住。
表单里必须用 @csrf,别手写 csrf_token()
Blade 中直接写 @csrf 是最稳的方式。它会自动生成 <input type="hidden" name="_token" value="xxx">,且和当前 session 绑定。手写 {{ csrf_token() }} 或 <?php echo csrf_token(); ?> 容易出错:
- 多次调用
csrf_token()可能触发新 token 生成,导致页面里渲染的值和 session 里不一致 - 如果表单被缓存(比如 Nginx 缓存 HTML、浏览器强缓存),
csrf_token()的值就过期了,但@csrf本身不会缓存——它每次响应都重算 -
@csrf还可能在未来版本中加入额外防护逻辑(比如绑定时间戳或 IP),手写方式无法自动继承
AJAX 请求必须读 meta[name="csrf-token"],不能每次调 csrf_token()
Laravel 默认在 插入:<meta name="csrf-token" content="{{ csrf_token() }}">。JS 应该从这里取值,而不是在发送请求时再调一次 csrf_token():
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
- 动态加载内容(如 HTMX、Turbo)后,旧的
meta值可能已失效,需监听 DOM 更新并重新读取 - Axios 示例:
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); - jQuery 示例:
$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); - 若用 Fetch,需手动在
headers里加:'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
VerifyCsrfToken::$except 只认 URI 路径前缀,不支持正则或路由名
豁免路径写在 app/Http/Middleware/VerifyCsrfToken.php 的 $except 数组里,格式非常严格:
- 写
'api/webhook/stripe'✅,匹配/api/webhook/stripe和/api/webhook/stripe?foo=bar - 写
'webhook/*'✅,匹配/webhook/notify、/webhook/callback - 写
'webhook.'❌,通配符只认*,不支持.或正则语法 - 写
'webhook.handle'❌,$except匹配的是请求 URI,不是路由名 - 写
'/webhook'和'webhook'效果一样,末尾斜杠不影响匹配
本地正常、线上 419?先查 session 是否真正写入
CSRF token 存在 session 里,token 失效本质是 session 丢了或没同步:
- Nginx 配置了
proxy_cookie_path / "/";但 Laravel 的config/session.php中domain设成了.example.com,而用户访问的是www.example.com—— cookie 不携带 - Redis 驱动连不上(权限、密码、端口错),退化成
file驱动但storage/framework/sessions目录不可写 - PHP-FPM 的
session.save_path被覆盖,或session.gc_maxlifetime设得太小(比如 60 秒),token 刚生成就过期 - 同一域名下多个 Laravel 应用共用 session 名称(
session.cookie_name),互相覆盖 token
最易被忽略的一点:CSRF 校验完全依赖 session 生命周期,不校验时间戳、IP 或 User-Agent。只要 session 活着,token 就一直有效——所以 session 配置比 token 本身更关键。










