PAYATHON 2026

Docker 中 Next.js middleware 携带 Cookie 请求后端为何返回 401?

数据阿明

后端返回 401,并不能直接证明 Cookie 没有发送。代码已经显式设置了 cookie 请求头,接下来需要确认请求是否到达预期的后端,以及后端能否用收到的 s.id 找到有效 session。仅凭现有代码,还无法确定唯一原因。

credentials 不会替 middleware 自动携带浏览器会话

Next.js middleware 中的 fetch 在服务端运行,不会自动继承浏览器请求中的 Cookie。credentials: "include" 不能代替显式转发 Cookie 请求头,也不会为这次请求建立与浏览器共享的 Cookie 存储。

你已经显式转发了 Cookie,继续调整 credentials 通常不能解决问题。后端的 CORS credentials: true 主要影响浏览器跨源请求,无法让后端自动识别 middleware 发来的 session。

先确认 Docker 内部请求地址

检查 API_ENDPOINTS.MIDDLEWARE_ME 的实际值。

在常见的 Docker 独立网络环境中,前端容器里的 localhost 指向前端容器自身。宿主机可以访问的 localhost:3001,不一定是前端容器应使用的后端地址。

如果 Docker Compose 中的两个服务处于同一网络,后端服务名为 backend,并在容器内监听 3001,可以使用以下内部地址:

http://backend:3001/实际的当前用户接口路径

这里要使用后端的容器端口。如果端口映射为 3001:8080,内部访问通常应使用 backend:8080。后端还需要监听容器网络可访问的地址,例如 0.0.0.0;如果进程只监听容器内的 127.0.0.1,其他容器通常无法访问。

地址错误常见的结果是连接失败。既然已经收到 401,也要确认响应来自预期的后端接口,而非其他服务或重定向后的地址。

可以直接读取入站请求的原始 Cookie 请求头,减少重新序列化带来的排查变量。这种转发方式更直接,但不代表现有的 request.cookies.toString() 必然有错。

将 middleware 中的调用改为:

const response = await getMeFromMiddleware(
  request.headers.get('cookie') ?? ''
);

然后让获取当前用户的函数区分“未登录”和其他故障:

async function getMeFromMiddleware(
  cookies: string
): Promise<User | null> {
  // 此地址应指向前端容器可访问的后端接口。
  const response = await fetch(API_ENDPOINTS.MIDDLEWARE_ME, {
    headers: {
      cookie: cookies,
    },
    cache: 'no-store',
    redirect: 'manual',
  });

  if (response.status === 401) {
    return null;
  }

  if (!response.ok) {
    throw new Error(
      `Current-user request failed: ${response.status}`
    );
  }

  return (await response.json()) as User;
}

这段代码沿用项目已有的 User 和 API_ENDPOINTS 定义。cache: 'no-store' 用于明确避免缓存当前用户查询,不能修复无效 session。redirect: 'manual' 用于排查接口是否发生重定向,避免只看到最终响应。

原来的 fetchApi(..., true) 会把连接失败、401、500 和 JSON 解析失败都转成 null,随后 middleware 统一跳转登录页,掩盖了故障类型。生产环境可以再为后端不可用等情况增加适合项目的错误处理。

middleware 中看到了正确 Cookie,只能证明浏览器到前端这一段存在该值。还需要在后端当前用户接口入口处确认:

  1. 请求是否到达预期的后端实例和路由。
  2. 后端收到的 Cookie 请求头是否包含预期的 s.id。
  3. session 中间件是否在认证处理之前执行,读取的 Cookie 名称是否一致。
  4. Cookie 解析、签名校验和 session 查询分别在哪一步失败。

调试时避免记录完整 Cookie、session ID 或签名密钥。可以记录 Cookie 是否存在、后端实例标识和会话查询结果。

如果后端已经收到正确的 Cookie,接下来应检查会话配置,无需继续修改 CORS。若使用服务端 session,可以排查以下情况:

  • session 已过期,或后端重启后内存中的会话丢失。
  • 登录请求与当前用户查询落到不同实例,而实例之间没有共享 session 存储。
  • 如果 Cookie 使用签名,不同实例使用了不同的签名密钥。
  • 登录接口和当前用户接口连接了不同的存储环境、数据库或命名空间。

现有材料没有提供后端框架和 session 配置,这些可能性都需要验证,不能直接认定其中某一项就是原因。

只有在 middleware 收不到 s.id 时,才需要优先检查浏览器保存和发送 Cookie 的条件,例如 Domain、Path、Secure 和 SameSite。

Cookie 不按端口隔离;localhost:3001 与 localhost:3002 端口不同,本身不能解释 Cookie 缺失。HttpOnly 限制浏览器 JavaScript 读取 Cookie,但不会阻止 middleware 从入站请求中读取它。

如果后端通过当前用户接口刷新 session 并返回 Set-Cookie,这次服务端 fetch 的响应头不会自动传给浏览器。需要刷新会话时,应另外处理返回给浏览器的响应及 Cookie 属性。

对于这份代码,下一步最有价值的是在后端确认 s.id 的接收和校验结果,同时核对 API_ENDPOINTS.MIDDLEWARE_ME 的实际目标地址,以区分容器寻址、Cookie 转发和 session 失效问题。

备注:内容仅供参考。