根本原因是浏览器SameSite策略升级:Chrome 80+默认将未声明SameSite的Cookie视为Lax,跨域请求下Lax阻止发送;SameSite=None必须配合Secure=true(HTTPS),且XSRF-TOKEN和session cookie均需显式设置。

为什么 Laravel 的跨域 Cookie 总是丢失
根本原因不是 Laravel 配置错了,而是浏览器对 SameSite 属性的强制策略升级 —— Chrome 80+ 默认把未声明 SameSite 的 Cookie 当作 SameSite=Lax,而跨域请求(比如前端在 http://localhost:3000 调用 https://api.example.com)下,Lax 会直接阻止 Cookie 发送。即使你开了 withCredentials: true,后端也设置了 Access-Control-Allow-Credentials: true,Cookie 还是不带。
Laravel 中间件里怎么正确设置 SameSite=None
不能只改 config/session.php 里的 'same_site' => 'none' —— 这只影响 session cookie,且 Laravel 7+ 才支持该配置项;更重要的是:Chrome 要求 SameSite=None 必须配合 Secure=true(即只在 HTTPS 下发送),否则直接忽略该设置,降级为 Lax。
-
config/session.php中必须同时设:"secure" => env('SESSION_SECURE_COOKIE', true),
"same_site" => 'none' - 开发环境(HTTP)无法真正使用
SameSite=None,浏览器会拒绝;本地调试建议用ngrok或localhost(Chrome 对localhost有例外,允许SameSite=None+Secure=false,但仅限开发) - 若需动态判断环境,可在中间件中手动覆盖响应头:
return $next($request)->withCookie(cookie('XSRF-TOKEN')
->sameSite('None')
->secure(true));
跨域场景下 XSRF-TOKEN 和 session cookie 都要处理
很多人只改了 session cookie,却忽略了 Laravel 默认通过 XSRF-TOKEN Cookie + X-XSRF-TOKEN 请求头做 CSRF 防护。这个 Token 也是 Cookie,同样受 SameSite 限制。
-
XSRF-TOKEN默认由StartSession中间件写入,但它的SameSite不继承session.php配置,需显式干预 - 推荐在
app/Http/Middleware/TrustProxies.php后加一个中间件(如SetSameSiteForCsrf),在响应前重写该 Cookie:if ($response->headers->hasCookie('XSRF-TOKEN')) {
$cookie = $response->headers->getCookie('XSRF-TOKEN');
$response->headers->setCookie($cookie->withSameSite('None')->withSecure(true));
} - 确保前端 axios 实例已启用凭证:
axios.defaults.withCredentials = true
常见错误现象和验证方式
别只看 Network 面板里 Cookie 是否“出现”,要看它是否被浏览器实际发送出去 —— 关键看请求头里有没有 Cookie: 字段,以及响应头是否有 Set-Cookie 且含 SameSite=None; Secure。
- 错误现象:
Unauthenticated、CSRF token mismatch、登录后立刻 401 —— 很可能是 Cookie 没发过去 - 检查响应头:
Set-Cookie: laravel_session=xxx; expires=...; Max-Age=...; path=/; domain=.example.com; secure; HttpOnly; SameSite=None(缺Secure或拼写错SameSite都会失败) - Chrome 控制台 Application → Cookies 标签页里,如果某 Cookie 显示 “SameSite=None is not allowed without ‘Secure’” 就说明
Secure缺失或环境非 HTTPS
SameSite 是浏览器层面的硬性约束,Laravel 只负责生成响应头;配错一个字母、漏掉一个 Secure、或者开发时用 HTTP 却硬设 SameSite=None,都会让整个跨域认证链断裂。










