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

NextJS生产环境中NextResponse.redirect不触发问题求助

问题分析

你的核心问题是生产环境下登录成功设置Cookie后,客户端跳转被middleware拦截,延迟一段时间才恢复正常。这是因为middleware中错误地从响应对象(res)读取Cookie,而非从请求对象(req)读取,导致middleware无法识别客户端刚设置的Cookie,进而错误触发重定向逻辑。此外,客户端路由跳转的时机可能早于Cookie生效的时机,也会加剧这个问题。

解决方案

1. 修复middleware的Cookie读取逻辑

原代码中getCookie("token", { req, res })会优先读取响应对象的Cookie,但响应对象是新创建的NextResponse.next(),不存在登录后设置的token。正确的做法是仅从请求对象(req)读取客户端发送的Cookie:

import { getCookie } from "cookies-next";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export const config = {
    matcher: "/((?!api|_next/static|_next/image|assets|favicon.ico|verify-email).*)",
};

export async function middleware(req: NextRequest) {
    // 仅从请求对象读取Cookie,这是客户端实际发送的凭证
    const token = getCookie("token", { req });
    const isAuthRoute = req.nextUrl.pathname === "/auth";

    if (!token && !isAuthRoute) {
        return NextResponse.redirect(new URL("/auth", req.url));
    }

    if (token && isAuthRoute) {
        return NextResponse.redirect(new URL("/projects", req.url));
    }

    return NextResponse.next();
}

2. 优化客户端Cookie设置与跳转逻辑

如果修复middleware后问题仍存在,建议将Cookie设置逻辑移至API路由(更符合安全规范,同时确保Cookie立即生效),再执行跳转:

步骤1:创建登录API路由

// app/api/login/route.ts
import { NextResponse } from "next/server";

export async function POST(request: Request) {
    try {
        const { phone, password } = await request.json();
        // 调用后端验证接口获取token
        const authResponse = await fetch(process.env.AUTH_API_URL!, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ phone, password }),
        });

        if (!authResponse.ok) throw new Error("登录验证失败");
        const { token } = await authResponse.json();

        // 由API路由设置Cookie(安全建议:开启httpOnly、secure)
        const response = NextResponse.json({ success: true });
        response.cookies.set({
            name: "token",
            value: token,
            httpOnly: true,
            secure: process.env.NODE_ENV === "production",
            sameSite: "strict",
            path: "/",
            maxAge: 86400, // 设置过期时间
        });
        return response;
    } catch (error) {
        return NextResponse.json({ error: (error as Error).message }, { status: 401 });
    }
}

步骤2:更新客户端登录逻辑

"use client";

import { useMutation } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { useState } from "react";

export default function AuthPage() {
    const [phone, setPhone] = useState("");
    const [password, setPassword] = useState("");
    const router = useRouter();

    const loginMutation = useMutation({
        mutationFn: async () => {
            const res = await fetch("/api/login", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ phone, password }),
            });
            if (!res.ok) throw new Error("登录失败");
            return res.json();
        },
        onSuccess: () => {
            // 跳转前强制刷新路由,确保middleware读取最新Cookie
            router.refresh();
            router.push("/projects");
        },
        onError: (error) => {
            alert(error.message);
        },
    });

    const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();
        loginMutation.mutate();
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={phone}
                onChange={(e) => setPhone(e.target.value)}
                placeholder="手机号"
                required
            />
            <input
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="密码"
                required
            />
            <button type="submit" disabled={loginMutation.isPending}>
                登录
            </button>
        </form>
    );
}

3. 极端场景下的兜底方案

如果上述方案仍有问题,可改用页面刷新式跳转(强制发起新HTTP请求,确保Cookie被携带):

// 在onSuccess中替换router.push为:
window.location.href = "/projects";

这种方式会触发页面刷新,虽然体验略差,但能确保middleware读取到最新的Cookie。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:07:27