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 请求头,并保留错误差异
可以直接读取入站请求的原始 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 统一跳转登录页,掩盖了故障类型。生产环境可以再为后端不可用等情况增加适合项目的错误处理。
在后端确认 Cookie 是否被接收并成功校验
middleware 中看到了正确 Cookie,只能证明浏览器到前端这一段存在该值。还需要在后端当前用户接口入口处确认:
- 请求是否到达预期的后端实例和路由。
- 后端收到的
Cookie请求头是否包含预期的s.id。 - session 中间件是否在认证处理之前执行,读取的 Cookie 名称是否一致。
- Cookie 解析、签名校验和 session 查询分别在哪一步失败。
调试时避免记录完整 Cookie、session ID 或签名密钥。可以记录 Cookie 是否存在、后端实例标识和会话查询结果。
如果后端已经收到正确的 Cookie,接下来应检查会话配置,无需继续修改 CORS。若使用服务端 session,可以排查以下情况:
- session 已过期,或后端重启后内存中的会话丢失。
- 登录请求与当前用户查询落到不同实例,而实例之间没有共享 session 存储。
- 如果 Cookie 使用签名,不同实例使用了不同的签名密钥。
- 登录接口和当前用户接口连接了不同的存储环境、数据库或命名空间。
现有材料没有提供后端框架和 session 配置,这些可能性都需要验证,不能直接认定其中某一项就是原因。
如果入站请求本身没有 Cookie
只有在 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 失效问题。
备注:内容仅供参考。
