问题出现得很突然,日志倒是留得挺全。文章页标题在 useEffect 里通过 document.title 设置,服务端返回的 HTML 里 title 是站点默认值,GSC 里 300 篇的索引标题全都一样。
刚开始盯着「canonical 和 og:title 也在客户端设置,一并丢失」,折腾了半个多小时,排掉了。
真正麻烦的地方在于,几个原因的现象几乎一样。document.title 只在浏览器执行 JS 后生效,抓取拿到的是初始 HTML。少数情况下是没有 SSR 或预渲染,服务端输出的 head 是固定模板。
另外,路由切换不重新请求 HTML,直接改 DOM。
上来先看服务端返回的原始 HTML
curl -s https://www.example.com/post/1 | grep -o '<title>[^<]*</title>'
随后确认渲染后的标题
curl -s 'https://search.google.com/test/rich-results?url=https://www.example.com/post/1' -o rr.html
再往下改用 react-helmet 做服务端注入
npm i react-helmet-async && grep -rn 'Helmet' src/App.jsx | head
预渲染关键路由
npx prerender-spa-plugin --routes /post/1,/post/2
当时还开着另一个排查窗口,两边日志对照着看才定位到。
别只看报错消失,验证要多做一步。curl 拿到的 HTML 里 title 就是文章标题,GSC 网址检查的已渲染 HTML 与原始 HTML 一致。
这里还有个坑。客户端设 title 用户看得见蜘蛛看不见,这是 SPA 最常见也最容易忽略的一处。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
