You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 11 Sanctum跨域(CORS)错误排查求助

Laravel 11 + Sanctum SPA认证CORS问题排查与解决

问题场景

开发Laravel 11项目,使用Laravel Sanctum为Vue3单页应用(SPA)提供认证服务,通过Docker部署。后端地址为http://shop_backend.localhost:8084,前端地址为http://shop_frontend.localhost:8787,前后端分属不同子域名。请求后端/api/csrf-cookie接口获取CSRF Cookie时,触发CORS错误。

错误信息

Access to XMLHttpRequest at 'http://shop_backend.localhost:8084/api/csrf-cookie' from origin 'http://shop_frontend.localhost:8787' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

现有配置详情

.env配置

APP_NAME=SHOP
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://shop_backend.localhost:8084
FRONTEND_URL=http://shop_frontend.localhost:8787

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shop
DB_USERNAME=test
DB_PASSWORD=test

SESSION_DRIVER=cookie
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=.localhost

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=redis
CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

SANCTUM_STATEFUL_DOMAINS=shop_frontend.localhost:8787

config/app.php配置

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->statefulApi();
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

cors.php配置

<?php

return [
    'paths' => ['api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => [env('FRONTEND_URL')],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,
];

sanctum.php配置

<?php

use Laravel\Sanctum\Sanctum;

return [

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS')),

    'guard' => ['web'],

    'expiration' => 120,

    'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

    'middleware' => [
        'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
        'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
        'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
    ],

    'prefix' => 'api',
];

docker-compose.yaml配置

volumes:
  redis_data:

services:
  database:
    image: mysql:8.4
    container_name: mysql_shop
    environment:
      - MYSQL_DATABASE=shop
      - MYSQL_USER=user
      - MYSQL_PASSWORD=test
      - MYSQL_ROOT_PASSWORD=test
    volumes:
      - /var/backups/mysql8/shop:/var/lib/mysql
    ports:
      - "3306"

  app:
    container_name: shop_backend
    image: webdevops/php-nginx:8.3-alpine
    volumes:
      - .:/var/www
      - ./docker/nginx/main.conf:/opt/docker/etc/nginx/main.conf
    ports:
      - "8084:80"
    links:
      - database
      - redis
    working_dir: /var/www
    hostname: shop_backend.localhost
    environment:
      - "DB_PORT=3306"
      - "DB_HOST=database"
      - "DB_PASSWORD=test"
      - "DB_DATABASE=shop"
      - "REDIS_ENDPOINT=redis"

  node:
    container_name: shop_frontend
    image: node:lts
    working_dir: /app
    command: bash -c "npm i && npm run dev -- --port 8080 --host"
    volumes:
      - ../shop-frontend:/app
    ports:
      - "8787:8080"
    user: node
    hostname: shop_frontend.localhost

  # phpmyadmin
  phpmyadmin:
    image: phpmyadmin
    restart: always
    depends_on:
      - database
    ports:
      - "8095:80"
    environment:
      PMA_USER: root
      PMA_PASSWORD: test
      PMA_HOST: database
      PMA_ARBITRARY: 1

  redis:
    image: redis:alpine
    restart: unless-stopped
    volumes:
      - redis_data:/data

问题定位与修复方案

1. 扩展CORS覆盖路径

当前cors.php中paths仅配置['api/*'],虽/api/csrf-cookie匹配该规则,但为避免路由匹配异常,建议显式添加Sanctum CSRF路径:

'paths' => ['api/*', '/sanctum/csrf-cookie'],

开发环境可临时设置为['*'],快速验证是否为路径问题。

2. 确保CORS中间件正确应用

Laravel 11需手动将CORS中间件加入API路由组,修改config/app.php中的withMiddleware方法:

->withMiddleware(function (Middleware $middleware) {
    $middleware->statefulApi();
    // 添加CORS中间件到API组
    $middleware->api([
        \Illuminate\Http\Middleware\HandleCors::class,
    ]);
})

3. 清除Laravel配置缓存

修改配置后需清除缓存,确保新配置生效:

# 进入后端容器
docker exec -it shop_backend bash
# 清除配置缓存
php artisan config:clear
# 清除路由缓存
php artisan route:clear

4. 检查Nginx配置冲突

使用webdevops/php-nginx镜像时,自定义的main.conf可能添加了额外CORS头,与Laravel的CORS中间件冲突。需确保Nginx配置中不重复设置Access-Control-Allow-Origin等头,若必须设置,需与Laravel配置一致并添加always参数:

add_header Access-Control-Allow-Origin http://shop_frontend.localhost:8787 always;
add_header Access-Control-Allow-Credentials true always;

5. 前端Axios配置修正

Vue3项目中需开启Axios的withCredentials,确保Cookie能被携带:

import axios from 'axios';

axios.defaults.baseURL = 'http://shop_backend.localhost:8084/api';
axios.defaults.withCredentials = true;

// 请求CSRF Cookie的示例代码
async function getCsrfCookie() {
    await axios.get('/csrf-cookie');
}

内容的提问来源于stack exchange,提问作者user16010521

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:42:01