Firefox v117.0.1中Next.js+nookies过期Cookie未清除问题
问题描述
我正在开发一个使用nookies包处理Cookie的Next.js项目,项目包含两个带过期时间的Cookie。通过nookies.set(ctx, name, value, options)设置Cookie时,options中包含maxAge属性,预期Cookie过期后会被浏览器自动清除,但仅在Firefox v117.0.1中出现过期Cookie未清除的情况,其他浏览器表现正常。
自动登出后再次登录时,自定义AuthProvider组件读取到的token始终为undefined,登录后会被重定向回登录页,必须登录两次才能正常进入站点。该问题为近期出现,旧版Firefox无此情况。
相关代码
设置Cookie的代码
export const setUaaCookie = (access: string, refresh?: string): void => { const accessExpires = GetTokenExpireTime(access); const refreshExpires = GetTokenExpireTime(refresh); setCookie('access_token', access, { expires: accessExpires, path: '/', domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN, }); setCookie('refresh_token', refresh, { expires: refreshExpires, path: '/', domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN, }); request.setHeader('Authorization', `Bearer ${access}`); };
AuthProvider认证逻辑代码
const authenticate = (): void => { // 获取浏览器Cookie const cookies = parseCookies(); const hasToken = cookies.refresh_token; let isPrivateRoute = true; const publicRoutes = [ '/account/login', '/account/otp-login', '/account/signup', '/account/forget-password', ]; // 判断当前路由是公开路由还是私有路由 if ( publicRoutes.find(item => router.asPath.includes(item)) || router.asPath === '/' ) isPrivateRoute = false; if (isPrivateRoute && !hasToken) { goToLoginPage(); setLoading(true); return; } if (!isPrivateRoute && hasToken) { const next = router.query.next || '/market-summary'; router.push(next.toString()); setLoading(true); return; } setLoading(false); }; useEffect(() => { authenticate(); }, [router]);
疑问
- 该问题的原因是什么?
- 如何在Next.js和Firefox v117.0.1中解决
nookies相关的Cookie清除问题? - 如何确保过期或已删除的Cookie从浏览器彻底清除,不影响自动登出功能?
解决方案
问题根源分析
Firefox v117.0.1在Cookie处理上存在兼容性异常:
- 当
domain属性设置为带前缀的非顶级域名(如.example.com)时,Firefox可能无法正确识别expires参数,导致过期Cookie未被清理。 nookies在新版本Firefox中可能存在expires与maxAge参数转换的bug,导致过期时间未被正确写入。- 自动登出后的重定向流程中,Firefox存在Cookie读取缓存,新设置的Cookie无法被及时读取,引发二次登录问题。
具体解决步骤
统一Cookie过期时间配置
同时设置expires和maxAge属性,避免单一参数在Firefox中失效,同时补充secure和sameSite属性增强兼容性:export const setUaaCookie = (access: string, refresh?: string): void => { const accessExpires = GetTokenExpireTime(access); const refreshExpires = GetTokenExpireTime(refresh); // 计算maxAge(秒数) const accessMaxAge = Math.floor((accessExpires.getTime() - Date.now()) / 1000); const refreshMaxAge = Math.floor((refreshExpires.getTime() - Date.now()) / 1000); setCookie('access_token', access, { expires: accessExpires, maxAge: accessMaxAge, path: '/', domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', }); setCookie('refresh_token', refresh, { expires: refreshExpires, maxAge: refreshMaxAge, path: '/', domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', }); request.setHeader('Authorization', `Bearer ${access}`); };主动清除过期Cookie
在AuthProvider初始化时,主动检查并清除已过期的Cookie:const clearExpiredCookies = () => { const cookies = parseCookies(); Object.entries(cookies).forEach(([name, value]) => { const expireTime = GetTokenExpireTime(value); if (expireTime && expireTime < new Date()) { destroyCookie(null, name, { path: '/', domain: process.env.NEXT_PUBLIC_COOKIE_DOMAIN, }); } }); }; useEffect(() => { clearExpiredCookies(); authenticate(); }, [router]);修复登录重定向逻辑
登录成功后,等待Cookie写入完成再执行重定向,规避Firefox的缓存问题:// 登录成功回调中 setUaaCookie(accessToken, refreshToken); setTimeout(() => { router.push(router.query.next || '/market-summary'); }, 100);修正domain属性格式
- 本地开发时可省略
domain,或设置为localhost - 生产环境设置为不带前缀的域名(如
example.com,而非.example.com),避免Firefox域名匹配异常
- 本地开发时可省略
更新nookies包
升级到最新稳定版,修复已知兼容性bug:npm update nookies
内容的提问来源于stack exchange,提问作者Saeed Mansoori
相关产品推荐
相关产品推荐

