next下生成sitemap的笔记

事情是这样的。我们项目(一个 Next.js 16 的车商交易平台)上线前要确认 sitemap.xml 是谷歌能识别的格式。我在浏览器里打开 http://localhost:3000/sitemap.xml,页面刷出来一长串:

ruby 复制代码
https://guineemall.com/ 2026-10-16T05:14:27.224Z daily 1
https://guineemall.com/en/ 2026-10-16T05:14:27.224Z daily 1
https://guineemall.com/search 2026-10-16T05:14:27.224Z daily 0.9
...

贴出来给同事一看,大家都说"这不是纯文本吗?谷歌要的是 <urlset><url><loc> 那种 XML 啊"。

我当时也懵了。因为我的 src/app/sitemap.js 写法明明是返回对象数组:

js 复制代码
export default async function sitemap() {
  return [
    {
      url: 'https://guineemall.com/',
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1,
    },
    // ...更多
  ]
}

Next.js 的约定是:sitemap.js 里 export default 一个数组,框架自动帮你序列化成标准 urlset XML。这是官方文档写的,我也不该手写 XML 字符串(Next 16 里手写 return new Response(...) 会直接构建报错,别问我怎么知道的)。

所以问题来了:代码是对的,但浏览器里是纯文本。


我开始了自以为很专业的排查

第一步,绕过浏览器,直接看服务器到底吐了啥:

bash 复制代码
curl -s http://localhost:3000/sitemap.xml > %TEMP%\sm.xml && notepad %TEMP%\sm.xml

记事本里打开------<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">,标准的 XML。

嗯?服务器是对的。那浏览器为啥是纯文本?

我当时的第一反应是浏览器缓存。毕竟这页面我刷新过好几次了。于是我开始疯狂验证这个假设:

  • 让用户在浏览器 Ctrl+Shift+R 强刷 → 还是纯文本
  • 开无痕窗口 → 还是纯文本
  • 清站点缓存 → 还是纯文本

无痕窗口还纯文本,缓存假设当场破产。


然后我走向了更离谱的方向

既然不是缓存,那就是"服务器对浏览器和对 curl 返回了不同的东西"。这个想法一旦冒出来就停不下来了。

我开始怀疑是不是内容协商 :服务器看到 Accept: text/html 或者 Chrome 的 UA,就决定返回纯文本?于是我用 curl 把浏览器的完整请求头都模拟了一遍:

bash 复制代码
curl -s -i \
  -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
  -H "Sec-Fetch-Dest: document" \
  http://localhost:3000/sitemap.xml

返回还是 content-type: application/xml。

我又想到本机开着 Clash 代理(HTTP_PROXY=http://127.0.0.1:7897),会不会是代理在中间篡改?于是:

bash 复制代码
# 让 curl 也走代理,看会不会变纯文本
curl -s --proxy "http://127.0.0.1:7897" http://localhost:3000/sitemap.xml

还是 XML。代理也排除了。

接着我把本机所有监听端口撸了一遍:3000、8082(Java 后端)、30985、8050......一个个打过去,没有一个返回那种纯文本。项目里也确认了只有 src/app/sitemap.js 一个生成器,没有第二个 route,没有 middleware 拦截,没有 Service Worker。

到这一步我已经有点魔怔了。最后我使出了"杀进程 + 删 .next + 重启"的大招------因为 Next.js 的 sitemap 这类 metadata 路由在 dev 模式下不会热更新,我猜是旧编译产物赖在缓存里。

powershell 复制代码
# 结束旧 dev server
taskkill /PID 33632 /F
# 删掉整个 .next(PowerShell 强制删,绕过 bash 的删除钩子)
Remove-Item -Path '.next' -Recurse -Force

重启完再 curl,确实是 XML。我长舒一口气,以为找到了根因,还把"清 .next 就修好了"写进了项目日志。


真相往往很蠢

直到用户自己憋出来一句:

我刚才看错了,页面看起来像纯文本,但其实在开发者工具的 Elements 面板里,它展示的是完整的 XML 结构。

我当场愣住。

浏览器访问 .xml 文件时,本来就会把 XML 当纯文本渲染出来------你看到的就是原始标签,一行一行的,不是 HTML 那种"渲染后的页面"。 这跟"返回了纯文本格式"是两码事。

真正的判断标准是:打开 DevTools → Elements(元素) 面板(不是 Network 的 Response,也不是控制台打印),如果里面是一棵

markdown 复制代码
urlset
  └─ url
       ├─ loc
       ├─ lastmod
       ├─ changefreq
       └─ priority

的树,那就说明浏览器收到的 100% 是合法 XML 文档,谷歌爬虫也能正常解析。

我回头一看,我前面所有"死证"------curl 是 XML、notepad 是 XML、走代理是 XML、删 .next 是 XML------其实从一开始就在告诉我同一件事:服务器返回的就是正确的 XML。所谓"浏览器是纯文本",只是 XML 在浏览器里的默认显示方式而已。

而那个"删 .next 修好了",大概率是重启后我让用户重新打开/强刷了一次,他这回终于点开了 Elements 面板看,而不是盯着地址栏下面那坨原始标签。


几个我这次学到的、并想记下来的点

  1. 浏览器显示 XML 就是"纯文本外观" 。别被页面长得像 txt 骗了。要看它是不是真 XML,用 curl -s -i 看 Content-Type,或者直接开 DevTools Elements 看 DOM 树。

  2. curl 不会骗你,但浏览器显示会误导你。 排查这类问题时,curl -s -i http://localhost:3000/sitemap.xml | findstr content-type 比在浏览器里瞪眼可靠一百倍。

  3. Next.js 的 sitemap.js 就老老实实 return 数组。 别手痒去写 XML 字符串然后 return new Response(...),Next 16 会构建失败(response.some is not a function)。框架帮你序列化,这是特性不是限制。

  4. dev 模式改了 metadata 路由(sitemap/robots)不一定热更新。 这个怀疑其实不是全错------真遇到"改了代码页面没变",删 .next 重启是稳妥的。只是这次它背了锅,真正的问题在别处。

  5. 先点开 Elements 再看结论。 这次最大的教训就是:我花了一下午验证"服务器没错",却没在最开始就让用户看一眼 DevTools 的 Elements 面板。如果第一步就点开,十分钟就能结束战斗。


写完这篇我自己都觉得好笑。技术排查最怕的不是问题难,是你以为的问题根本不存在。下次再看到浏览器里 XML 长这样,我大概会先笑一下,然后顺手按 F12。

相关推荐
zach3 天前
RSC(React Server Components)详解
next.js
To_OC5 天前
同样是让 AI 写全栈,为什么别人一句话就够了
前端·前端框架·next.js
FungLeo9 天前
成为全栈·Next.js 网站前台篇·响应式、可访问性与错误状态:内容站不能只验桌面首页
react.js·响应式设计·next.js·移动端适配·成为全栈·web 可访问性
FungLeo10 天前
成为全栈·Next.js 网站前台篇·站点设置如何驱动页头、页脚与 SEO
react·isr·next.js·metadata·成为全栈·站点配置
FungLeo12 天前
成为全栈·Next.js 网站前台篇·同源 BFF 代理:API、附件、Cookie 与跨站写入保护
react·csrf·next.js·bff·成为全栈·route handler
FungLeo12 天前
成为全栈·Next.js 网站前台篇·评论系统:叠楼、回复、删除与内容审核如何落到前台
react.js·内容审核·next.js·评论系统·树形数据·成为全栈
cindershade15 天前
一条请求的接力赛:用运行时责任重画 RSC 边界
性能优化·next.js
zach16 天前
前端落地实战:Next.js 项目 Nginx + PM2 生产部署全流程(附踩坑总结)
前端·pm2·next.js
zach16 天前
Vue/React SPA 打包部署后,子路由刷新 401 未登录问题彻底解决
前端·nginx·next.js