Next.js 网站技术 SEO 检查清单(22 项,可逐条执行)
上线前逐条过一遍:可抓取性、内容结构、结构化数据、性能、国际化五大类共 22 项。每项给出检查命令、判断标准和修复方向,全部来自真实项目的实测。
这份清单来自实际项目:一个 126 页的站点改版,加上后来的双语重构。每一条都是踩过之后留下的。
用法:逐条过,打勾或标记问题。不要跳着看——第 1–8 条是基础,基础错了后面全白搭。
一、可抓取性(8 项)
1. 首轮 HTML 有没有正文
这是最容易出问题的一条。
curl -s https://your-site.com/page | python3 -c "
import sys, re, html
raw = sys.stdin.read()
m = re.search(r'<main[^>]*>(.*?)</main>', raw, flags=re.S|re.I)
seg = m.group(1) if m else raw
t = re.sub(r'<(script|style)[^>]*>.*?</\1>', ' ', seg, flags=re.S|re.I)
t = re.sub(r'<[^>]+>', ' ', t)
print('正文长度:', len(re.sub(r'\s+', ' ', html.unescape(t)).strip()))
"
判断:低于 500 字符就是有问题。真遇到过只有 46 个字符的页面(内容是 Loading…)。
原因:客户端渲染(CSR)。搜索引擎首轮不执行 JS,看到的是空壳。
修复:改用服务端组件,或在 page.tsx 里直接取数据渲染。
2. robots.txt 有没有误伤
curl -s https://your-site.com/robots.txt
判断:确认没有 Disallow: /,且 sitemap 地址正确。
坑:改版时把 /llms.txt 或 /sitemap.xml 挡掉。我遇到过 middleware 白名单没包含 .txt 的情况,导致对 AI 声明「本站已下线」。
3. sitemap 是否完整且可访问
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" https://your-site.com/sitemap.xml
curl -s https://your-site.com/sitemap.xml | grep -c "<loc>"
判断:200,条数与实际页面数一致。
坑:lastmod 用 new Date()。每次部署全部 URL 都变成「今天更新」,Google 会判定不可信并最终忽略这个字段。要用内容真实更新日期。
4. 状态码是否正确
for u in / /about /nonexistent-page; do
printf "%-20s " "$u"
curl -s -o /dev/null -w "%{http_code}\n" "https://your-site.com$u"
done
判断:正常页 200,不存在页 404。
坑:把所有未知路径都返回 410。410 的语义是「永久删除」,用户拼错地址也返回 410,等于告诉搜索引擎这是有意删除,同时失去错误监控信号。
正确做法:确认删除的用 410,其他用 404。
5. canonical 是否自指
curl -s https://your-site.com/page | grep -o '<link rel="canonical"[^>]*>'
判断:指向自己,且是最终 URL(不是重定向前的)。
坑:/services/en 308 跳到 /en,但 canonical 写 /services/en。Google 会索引一个永远 308 的地址。
6. 有没有意外的 noindex
curl -s https://your-site.com/page | grep -o '<meta name="robots"[^>]*>'
curl -sI https://your-site.com/page | grep -i x-robots-tag
判断:正文页应该是 index, follow。
7. 重定向链是否只有一跳
curl -sIL https://your-site.com/old-page | grep -E "^HTTP|^location"
判断:不要出现 A → B → C 的链。
8. 静态资源是否被拦
curl -s -o /dev/null -w "%{http_code}\n" https://your-site.com/_next/static/chunks/main.js
判断:200。
坑:middleware 拦截规则写得过宽,把 /_next/static 也拦了,页面白屏。
二、内容结构(4 项)
9. H1 有且只有一个
curl -s https://your-site.com/page | grep -c "<h1"
判断:恰好 1 个。
为什么重要:我在 19 个美国中小企业站的抽样里发现,只有 7/13 恰好一个 H1,3 个完全没有——标题层级是搜索引擎判断页面主题的直接信号。
10. H2 有没有分节
判断:长文应该有 H2 分节,纯文本堆砌不利 AI 摘取。
坑:用 text-2xl font-bold 的 <div> 假装标题。视觉一样,机器读不出来。
11. 标题和描述有没有重复
curl -s https://your-site.com/sitemap.xml | grep -o '<loc>[^<]*' | sed 's|<loc>||' | while read u; do
t=$(curl -s "$u" | grep -o '<title>[^<]*' | head -1)
echo "$t | $u"
done
判断:每页 title 唯一。
12. 描述长度是否合适
判断:60–155 字符。太短浪费,太长被截断。
Next.js 里可以用 description.slice(0, 155) 统一控制。
三、结构化数据(4 项)
13. 有没有 JSON-LD
curl -s https://your-site.com/page | grep -o '"@type":"[A-Za-z]*"' | sort | uniq -c
判断:至少有一个与页面类型匹配的 schema。
| 页面类型 | 应该有的 schema |
|---|---|
| 首页 | Organization、WebSite |
| 服务页 | Service、FAQPage |
| 文章页 | TechArticle / Article、BreadcrumbList |
| 商品页 | Product、Offer |
14. FAQPage 有没有标记
你页面上有 FAQ 折叠块,但没有 FAQPage schema,AI 就提取不到问答对。
修复:把 mainEntity 的 Question/Answer 结构加进去。
15. 实体是否声明了 provider
Service 里的 provider 要指向站点级的 Organization:
{
"@type": "Service",
"provider": { "@id": "https://your-site.com/#organization" }
}
用 @id 引用而不是重复声明实体——避免同一实体出现多个版本。
16. 报价有没有写进 schema
"offers": [
{ "@type": "Offer", "name": "入门", "price": "8000", "priceCurrency": "CNY" }
]
为什么:AI 回答「这个服务多少钱」时,有结构化报价的页面更容易被引用。
四、性能(4 项)
17. 核心指标是否达标
用 Lighthouse 实测,不是估算:
npx lighthouse https://your-site.com --output=json --output-path=lh.json --chrome-flags="--headless=new"
| 指标 | 目标 |
|---|---|
| LCP | < 2.5s |
| CLS | < 0.1 |
| TBT | < 200ms |
18. 图片是否用了现代格式
curl -s https://your-site.com/page | grep -o 'src="[^"]*\.\(png\|jpg\)"' | head -5
判断:界面截图应该用 WebP。
实测数据:四张 PNG 合计 955 KB,转 WebP 后 105 KB(11%)。纯色截图的压缩比能到 6%。
19. 有没有阻塞渲染的脚本
判断:首屏不应有同步 <script> 做布局修改。
真实案例:一段在 <head> 里改 lang 属性的脚本,让 TBT 从 60ms 涨到 240ms。改成静态标记后掉回 60ms。
20. 字体有没有本地化
坑:直接 @import 一个 8–10 MB 的中文字体包,LCP 直接崩。用系统字体栈,或只加载用到的子集。
五、国际化(2 项)
21. html lang 是否与页面语言一致
for u in / /en; do
printf "%-6s " "$u"
curl -s "https://your-site.com$u" | grep -o 'lang="[a-zA-Z-]*"' | head -1
done
判断:/ 是 zh-CN,/en 是 en。
这是最硬的一条。Next.js App Router 里 <html lang> 只能由根 layout 定义一次,要按路由切换必须拆 route group:
app/(zh)/layout.tsx <html lang="zh-CN">
app/(en)/layout.tsx <html lang="en">
用客户端脚本改 document.documentElement.lang 是无效的——搜索引擎首轮看到的是错的。
22. hreflang 是否双向且含 x-default
curl -s https://your-site.com/en | grep -o 'hrefLang="[^"]*"'
判断:每个语言版本都要列出全部语言,包括自己。另外要有 x-default 指向默认版本。
坑:页面级 metadata 会整段覆盖 layout 的 alternates,不是合并。重构后 /en 的 hreflang 消失就是这个原因。
快速自查
如果只有五分钟,查这四条:
curl首页看正文长度(第 1 条)curl /robots.txt(第 2 条)curl /sitemap.xml | grep -c loc(第 3 条)- 查 html lang(第 21 条)
这四条出问题,其余优化都没有意义。
用工具代替手工
上面 22 条里,1、2、3、9、13、18 可以自动检查:
网站技术体检工具 —— 输入域名 30 秒出结果,不需要注册。
如果查出来问题较多、想系统修一遍,技术 SEO 体检与修复服务里写了完整流程和价格。