SPA单页面应用、前后端分离项目SEO优化的方法
Category(分类): Vue Status: 已校订(历史方案 + 2026 现代 SEO)
前言
原作者的博客采用前后端分离:前端通过 Ajax 获取接口数据,再在浏览器中渲染。早期搜索引擎对 JavaScript 的渲染能力有限,因此原作者观察到收录页面只包含 HTML 中写死的文字。


这段经历有历史价值,但到 2026 年需要修正两点:
- Google 可以渲染 JavaScript。 Googlebot 使用 evergreen Chromium,处理大致经历 crawling → rendering → indexing;但渲染可能排队,且会受到资源阻断、接口失败、超时、兼容性和性能影响。并非所有爬虫都可靠执行 JavaScript。
- SPA 不是天然无法 SEO。 可抓取的真实链接、稳定 URL、可访问资源、正确状态码和渲染结果都很重要。不过对于文章、营销页等公开内容,让关键正文直接出现在首个 SSR/SSG HTML 中通常更稳妥,也有利于真实用户首屏体验。
百度曾公布带渲染能力的 Baiduspider-render/2.0,因此不能再断言“百度蜘蛛执行不了 JS”;面向百度仍应以当前站长平台的抓取诊断、服务器日志和真实页面验证为准。
一、2026 年首选:SSR、SSG 与混合渲染
对公开文章站,优先级通常是:
- SSG / 构建时预渲染:内容发布时生成每个 URL 的完整 HTML,适合更新频率可控的文章;
- SSR:请求时由服务端返回页面 HTML,适合实时或个性化程度较低的公开数据;
- 混合渲染:按路由选择预渲染、SSR、缓存或 CSR;
- 纯 CSR:更适合登录后后台、强交互且不需要收录的页面。
SSR/SSG 并非没有成本:SSR 需要服务端资源和缓存策略,hydration 需要客户端 JavaScript;SSG 则要处理构建时长与内容更新。但它们避免让重要正文依赖搜索引擎第二阶段渲染,通常是内容站的现代首选。
Nuxt / Nitro 示例
Nuxt 4 默认支持 universal rendering,也可为特定路由设置预渲染规则:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/articles/**': { prerender: true },
'/admin/**': { ssr: false },
},
})
页面元信息可使用 useSeoMeta 和 useHead:
<script setup lang="ts">
const route = useRoute()
const article = await useFetch(
() => `/api/articles/${route.params.id}`,
)
useSeoMeta({
title: () => article.data.value?.title ?? '文章',
description: () => article.data.value?.summary ?? '',
ogTitle: () => article.data.value?.title ?? '文章',
})
useHead(() => ({
link: [
{
rel: 'canonical',
href: `https://example.com/articles/${route.params.id}`,
},
],
}))
</script>
实际项目还要处理不存在文章的 404、数据获取失败和 HTML 转义,不能让所有未知 ID 都返回同一份 200 页面。
二、页面级 SEO 基线
渲染方式只是起点,每个可索引页面还应做到:
- 规范 URL:为每份内容选定唯一、稳定的 canonical URL;旧地址用 301 合并。
- 元信息:独立、描述性的
<title>与 description;社交分享可补 Open Graph。 - 结构化数据:按内容使用符合 Google 规范的 JSON-LD(如
Article),字段必须与页面可见内容一致。 - 可抓取链接:站内导航输出真实
<a href="/articles/...">,不要只靠 click handler 或#/...。 - Sitemap 与 robots:sitemap 只列规范、可索引 URL;
robots.txt不要误挡渲染所需的 JS、CSS、图片和 API。 - 正确状态码:不存在页面返回 404,永久迁移返回 301;不要把错误页统一返回
200 index.html,否则可能形成 soft 404。 - 可访问正文:标题、正文、关键链接最好在首个 SSR/SSG HTML 中;延迟加载不能依赖点击或滚动才让爬虫看到主要内容。
- 性能与稳定性:控制 JavaScript 体积,确保服务端和 API 可靠,避免渲染阶段出现控制台或网络错误。
部署后应使用 Google Search Console URL Inspection、Rich Results Test、抓取日志和实际 HTML 检查,而不是仅凭搜索截图判断。
三、URL:path 与 query 都可以规范使用
原文认为 /articles/?id=xxx 天生不友好、爬虫只喜欢 /articles/xxx,这个结论过于绝对。Google 可以抓取规范的 query URL。可读 path 往往更利于用户理解和分享,但决定因素是 URL 稳定、可发现、无重复和有统一 canonical。
如果两个地址都能访问同一篇文章:
/articles/123
/articles/?id=123
应选一个作为公开 canonical,并用 301 或 rel="canonical" 合并信号。Nginx 内部 rewrite 可以兼容旧后端,但 rewrite 本身不等于 SEO 优化:
# 历史兼容示意:公开 URL 仍是 /articles/123
rewrite ^/articles/(\d+)$ /articles/?id=$1 break;
还必须保证不存在的 ID 返回 404,并避免两个入口长期产生重复内容。
四、原 Prerender + Nginx 方案
历史方案,请勿直接照抄。 以下按原作者当年的实践恢复安装、守护、分流、测试和 rewrite 上下文,仅用于理解遗留 dynamic rendering。相关仓库、依赖、无头浏览器与 Nginx 安全边界都可能已变化;新项目优先采用前文 SSR/SSG。
原方案启动一个 Prerender 服务,用无头浏览器执行页面 JavaScript并返回 HTML;Nginx 按 User-Agent 判断爬虫,把爬虫请求转给 Prerender,普通用户仍访问 CSR 页面。这属于 dynamic rendering(动态渲染),不要与“构建时为所有用户生成同一份静态 HTML”的 SSG 混淆。
4.1 安装、启动与 curl 验证(历史命令)
原文要求先有 Node.js 和 Git 环境,然后执行:
# 历史记录,请勿直接作为现行安装步骤
# 该仓库路径/启动方式在当前版本可能已失效
git clone https://github.com/prerender/prerender.git
cd prerender
npm install
node server.js # 原文默认监听 3000 端口
启动后,原作者用完整站点 URL 请求本地渲染服务;若返回执行 JavaScript 后的正文,就认为服务已启动:
# 将“你的网站全路径”替换为带协议的实际 URL
curl http://localhost:3000/你的网站全路径

这只能验证当时本机服务的一次输出,不代表状态码、缓存、资源加载和搜索引擎收录均正确。
4.2 forever 进程守护(历史命令)
原作者指出,终端关闭后直接运行的 Node.js 进程会退出,于是使用当年的 CLI 工具 forever 保持 server.js 运行:
# 历史方案,请勿直接照抄
npm install forever -g
forever start server.js
forever list
forever stop server.js
forever restartall
实际操作是在 prerender 根目录运行 forever start server.js。

forever 不解决服务健康检查、日志轮转、安全更新、缓存、状态码或自动回滚;现代部署应按运行环境使用受维护的进程/容器编排能力。
4.3 完整 Nginx bot 分流(历史配置)
原作者让普通用户访问原 CSR 地址,让列出的爬虫 User-Agent 代理到 Prerender:
# 历史方案,请勿直接照抄
location / {
# 表示是否需要代理
set $prerender 0;
set $prerender_url "http://127.0.0.1:3000";
if ($http_user_agent ~* "baiduspider|Googlebot|360Spider|Bingbot|Sogou Spider|Yahoo! Slurp China|Yahoo! Slurp|twitterbot|facebookexternalhit|rogerbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
set $prerender 1;
}
if ($prerender = 1) {
proxy_pass $prerender_url;
rewrite ^(.*)$ /https://$host$1 break;
}
}
配置后,原文使用 nginx -s reload 重载。这里恢复的是历史原貌,不表示这种 if、变量 proxy_pass、协议写死和 UA 清单适合当前生产环境。
4.4 两组 curl 测试(原作者当时的比较)
普通用户请求:
curl https://你的网站全路径
原作者当时观察到返回的是未执行页面脚本的壳内容:

模拟 Googlebot 请求:
curl -H 'User-agent:Googlebot' https://你的网站全路径
原作者当时观察到请求命中 Prerender,返回了解析后的正文:

这两组命令只能证明配置对给定 UA 的分流结果,不能证明请求者是真实 Googlebot,也不能证明页面最终会抓取、渲染、索引或获得排名。
4.5 文章 URL rewrite(历史配置)
原作者希望把公开地址从 /articles/?id=xxx 表现为 /articles/xxx,先给普通访问增加内部 rewrite:
rewrite ^(.*)/articles/(\d+)$ /articles/?id=$2 break;
然后分别处理爬虫的快照 URL 和普通用户的 REST 风格地址:
# 历史方案,请勿直接照抄
if ($prerender = 1) {
proxy_pass $prerender_url;
rewrite ^(.*)/articles/(\d+)$ /https://$host/articles/?id=$2 break;
rewrite ^(.*)$ /https://$host$1 break;
}
rewrite ^(.*)/articles/(\d+)$ /articles/?id=$2 break;
浏览器地址栏保留 /articles/xxx,服务器内部访问旧 query 入口。原作者随后测试了该 rewrite:

但 query URL 并非天然不友好,rewrite 本身也不等于 SEO 优化;仍要处理 canonical、重复入口、重定向策略和不存在 ID 的 404。
4.6 原作者当时的最终效果
原作者根据搜索结果截图认为 Google 收录效果明显改善,并把它作为个人实践结论:

这是历史个案,不能由一张搜索截图推导出通用因果关系,排名也会受内容、链接、抓取、性能等多种因素影响。
五、动态渲染的现代风险与过渡边界
Google 现在明确把 dynamic rendering 定义为 workaround,而不是推荐的长期方案。旧文中的仓库启动路径在资料核查时已不可直接验证,forever 也只是旧式 Node 进程守护工具。
按 User-Agent 分流还存在以下风险:
- User-Agent 可伪造,bot 列表会过时;真实 Googlebot 应通过反向 DNS 或官方 IP ranges 验证;
- 为 bot 和用户维护两套渲染路径,会增加缓存不一致和故障面;
- 内容实质不同可能构成 cloaking;
- 快照可能缓存 API 错误、“加载中”或过期内容。
若遗留站确实暂时无法迁移 SSR/SSG,并且目标爬虫不能执行 JavaScript,可按当前供应商文档把动态渲染作为过渡层,但必须审计和锁定依赖,配置鉴权、缓存与失效,处理超时、监控、回滚和一致状态码,并保证用户与爬虫看到的主要内容实质相似,最终迁移到 SSR/SSG。
六、部署检查清单
无论采用 Nuxt SSR、SSG 还是遗留 dynamic rendering,都应检查:
- 根地址与文章深层 URL 首次直达;
- 站内导航和深层 URL 刷新;
- 未知页面是否返回真实 404,而不是 200 shell;
- API、JS、CSS、图片、
robots.txt、sitemap.xml是否可访问; - canonical、title、description 与结构化数据是否逐页正确;
- query/path 双入口和旧 URL 是否 301/规范化;
- SSR/快照是否包含实际正文,而不是 loading;
- 移动端、Search Console 渲染 HTML、网络资源和 JavaScript 错误;
- 缓存发布后是否会让 HTML 引用已删除的哈希资源。
总结
- Google 能渲染 JavaScript,但存在渲染队列、资源和失败风险;其他爬虫能力不一。
- 公开内容的现代首选是 SSR、SSG 或混合渲染;Nuxt/Nitro 已提供直接路径。
- Dynamic rendering / Prerender + bot 分流只适合遗留系统的过渡兼容,不再是 Google 推荐方案。
- Query 参数不是天然不友好;应解决的是规范 URL、重复内容和状态码。
- SEO 还需要元信息、结构化数据、真实链接、sitemap、robots、性能和可验证的抓取结果共同配合。
官方参考
- Google:JavaScript SEO 基础
- Google:动态渲染是 workaround
- Google:URL 结构
- Google:合并重复 URL / canonical
- Google:创建并提交 sitemap
- Google:验证 Googlebot
- Nuxt 4:Rendering Modes
- Nuxt 4:Prerendering
- Nuxt 4:SEO and Meta
- 百度:Spider 新增渲染抓取 UA(历史资料)
作者:AnLingYi 原文链接:https://juejin.cn/post/6844903789661519886 来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。