Next.js App Router预渲染报错:因使用headers触发动态服务器错误
问题描述
在Next.js App Router项目中预渲染/pricing-plans页面时触发以下错误:
Error: Dynamic server usage: Page couldn't be rendered statically because it used `headers`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error at o (D:\upwork\movein\new frontend\muuve-frontend\.next\server\app\(website)\pricing-plans\page.js:1:13234) at o (D:\upwork\movein\new frontend\muuve-frontend\.next\server\app\(website)\pricing-plans\page.js:1:12434) Error occurred prerendering page "/pricing-plans". Read more: https://nextjs.org/docs/messages/prerender-error
注释掉getAgentPricingPlansAction调用后页面可正常运行。
代码片段
页面组件
import React from 'react'; import PricingPlansMain from '@/components/pages/PricingPlansPage'; import { getMeAction } from '@/server/auth-actions'; import { getAgentPricingPlansAction } from '@/server/payment-actions'; import PageLayout from '@/components/ui/PageLayout'; import UserCtxLayoutProvider from '@/components/ui/UserCtxLayoutProvider'; import styles from '@/components/pages/PricingPlansPage/styles.module.css'; export default async function PricingPlansPage() { const userData = (await getMeAction())?.success; const plans = await getAgentPricingPlansAction(); return ( <UserCtxLayoutProvider userData={userData}> <PageLayout headerClass={styles.header} headerTheme='light' logoConfig={{ theme: 'black', variant: 'full' }} headerVersion='full' footerTheme='default' > <PricingPlansMain plans={plans} /> </PageLayout> </UserCtxLayoutProvider> ); }
操作函数
export const getMeAction = async () => { try { const res = await fetch(`${process.env.BACKEND_BASE}/users/me`, { cache: 'no-store', headers: { ...getAuthorizationHeader() }, next: { tags: [getCacheKey('userData')] }, }); if (!res.ok) { throw new Error('server_error'); } const { data } = await res.json(); return { success: data as UserType }; } catch (err) { return { error: 'server_error' }; } }; export async function getAgentPricingPlansAction() { try { const res: Response = await fetch( `${process.env.BACKEND_BASE}/payments/plans/prices`, { method: REQUEST_METHODS.GET, headers: { ...getNodeRequiredRequestHeaders(), ...getAuthorizationHeader(), }, } ); if (!res.ok) { throw new Error('unexpected error'); } const data: PricingPlansActionResponseType = await res.json(); return data; } catch (err: any) { throw new Error(err.message as string); } }
原因分析
错误核心是:页面预渲染时尝试访问请求头(headers),但静态预渲染的页面无法获取动态请求上下文。getAgentPricingPlansAction中调用的getNodeRequiredRequestHeaders或getAuthorizationHeader函数,应该使用了Next.js的headers()方法——这个方法属于动态函数,仅能在动态渲染的页面/组件中使用。当Next.js尝试静态预渲染页面时,检测到这些动态函数调用,就会抛出该错误。
解决方案
1. 强制页面动态渲染
在页面组件文件顶部添加dynamic配置,告诉Next.js该页面必须动态渲染,不再尝试静态预渲染:
export const dynamic = 'force-dynamic'; // 添加该行代码 export default async function PricingPlansPage() { // ... 原有代码 }
这种方式简单直接,适合需要根据用户请求头(如授权信息)返回不同内容的页面。
2. 分离静态与动态逻辑(可选)
如果定价计划内容本身是静态的,不需要用户授权信息,可以修改getAgentPricingPlansAction,移除依赖headers()的请求头,或仅在有用户授权时才添加这些头:
export async function getAgentPricingPlansAction() { const headers: HeadersInit = { ...getNodeRequiredRequestHeaders() }; // 仅在存在授权信息时添加头(需确保getAuthorizationHeader不会强制调用headers()) const authHeader = getAuthorizationHeader(); if (authHeader) { Object.assign(headers, authHeader); } try { const res: Response = await fetch( `${process.env.BACKEND_BASE}/payments/plans/prices`, { method: REQUEST_METHODS.GET, headers, next: { revalidate: 86400 } // 静态内容可添加长期缓存策略 } ); if (!res.ok) { throw new Error('unexpected error'); } const data: PricingPlansActionResponseType = await res.json(); return data; } catch (err: any) { throw new Error(err.message as string); } }
这种方式允许页面在无用户授权时静态预渲染,有用户时动态渲染,但需要后端接口支持无授权访问定价计划。
内容的提问来源于stack exchange,提问作者Zain
相关产品推荐
相关产品推荐

