Next.js 本地 404 错误?静态博客文章消失的解决方案
你用 Next.js 搭好了博客,文章在线上跑得好好的。但在本地开发时访问 localhost:3000/blog/my-article,系统给你返回一个干净的 404。路由处理器在那儿。markdown 文件也在那儿。部署时什么都没坏。你就这样撞上了一个无声的坑:Next.js 开发服务器和生产环境处理静态文件的路径不一样。这种摩擦每次都要独立开发者花上几个小时才能搞清楚。
为什么会这样
Next.js 的开发服务器和生产版本对静态文件的处理方式完全不同。开发服务器从项目根目录运行,实时监听文件变化。生产版本会优化静态资产的传输路径,通过 .next/ 目录和 CDN 缓存。如果你的博客文章放在 public/blog/ 下,但你的 API 或页面组件期望它们在别的地方,或者开发服务器没有配置监听这些特定的文件,本地就会返回 404,但在生产环境里却工作正常。
特别是在不重新编译就在本地测试,或者路由配置假设了生产优化(而开发环境还没有这些优化)的时候,这个问题会变得更严重。
解决方案 1:直接从 Public 文件夹伺服
最简单的办法:把文章放在 /public/blog/ 里,直接伺服。
放在 public/blog/my-first-article.md 的文章可以通过 http://localhost:3000/blog/my-first-article.md 访问。但如果你的前端期望的是 /blog/my-first-article(没有 .md),你就需要一个动态路由来拦截并转换它。
创建这样一个动态路由:app/blog/[slug]/page.js
import fs from 'fs/promises';
import path from 'path';
export default async function BlogPost({ params }) {
const { slug } = params;
const filePath = path.join(process.cwd(), 'public', 'blog', `${slug}.md`);
try {
const raw = await fs.readFile(filePath, 'utf-8');
const html = parseMarkdown(raw); // 用你的 markdown 解析器
return <article>{html}</article>;
} catch {
return <div>404: 文章未找到</div>;
}
}
这个方案在开发和生产环境里的表现完全一样。缺点是:每次请求都要从磁盘读取文件,如果你有 100+ 篇文章,就应该考虑缓存结果或在构建时预加载。
解决方案 2:专用 API 路由(推荐用于规模化应用)
不依赖静态文件伺服,而是创建一个 API 端点,按需读取文章:
// app/api/blog/[slug]/route.js
import fs from 'fs/promises';
import path from 'path';
export async function GET(request, { params }) {
const { slug } = params;
const filePath = path.join(process.cwd(), '_content', `${slug}.md`);
try {
const content = await fs.readFile(filePath, 'utf-8');
return Response.json({ content, slug });
} catch (error) {
return Response.json({ error: '文章未找到' }, { status: 404 });
}
}
从你的页面组件里获取文章:
const res = await fetch(`/api/blog/${slug}`);
const { content } = await res.json();
为什么这个方案更好:
- 在本地开发和生产版本里完全一致,没有差异
- 你可以在 API 层后续添加过滤、搜索、分页或访问控制,而不用改动页面结构
- 内容存储(
_content/目录)和公共资产分离,减少混淆 - 在生产环境里更容易在边缘节点(edge)做缓存和监控
验证:在两个环境里测试
实现任何一个方案后,做这些检查:
- 开发模式:
npm run dev→ 导航到http://localhost:3000/blog/test-article→ 文章应该加载。检查终端是否有 404 错误。 - 生产构建:
npm run build && npm start→ 同一个 URL → 文章应该加载得一模一样。没有运行时的差异。 - 浏览器开发者工具的 Network 标签:确认文章从
/api/blog/或/public/blog/加载,不是从 404 响应返回。 - 硬刷新(
Ctrl+Shift+R或Cmd+Shift+R):清除缓存重新加载,抓住浏览器缓存的问题。
如果在开发环境看到 404,但在生产环境没有(或反过来),问题就在你的路由定义或文件路径上。检查这些:
- 文件是否真的存在于你要读取的路径(用
ls -la _content/验证) - 路由的 slug 是否匹配文件名(如果 slug 是
my-article,文件应该是my-article.md,不是my_article.md) - 路径里没有多余的斜杠
为什么这对你的产品很重要
你的博客是你 SaaS 的发行渠道。读者通过搜索、社交媒体或链接来找你,期望的是无缝体验。在开发环境里的 404 后来在生产环境修好了,这是一个隐藏的退步——你不会在用户发现之前就捕捉到死链、损坏的 markdown、缺失的元数据或性能问题。而用户发现了的话,他们就走了。
特别是在用博客作为潜在客户获取或客户入门渠道的时候,这一点更关键。每一个 404 都是一个掉的转化率,都是信誉的伤害。
下一步
Trove Deck Solution 在构建定制 SaaS 产品时,我们设计内容层——博客、文档、帮助中心——确保开发和生产环境是一样的。这种开发与生产的一致性在架构评审阶段就会被抓住,代码还没写呢就解决了。如果你在构建一个有内容或文档层的 SaaS,想避免这些坑,或者你已经准备好把产品想法变成现实,我们来聊聊。Trove Deck Solution 在这儿帮你有信心地发货。