一句话总结: Search Console 把某个 URL 标成 “Soft 404”,意思是这个页面看起来像”未找到”页面,但服务器却返回了 HTTP 200 OK。Google 抓了页面、发现没有真正的内容(或者只有”未找到”、“暂无结果”这类提示),于是决定当作 404 处理、不收录。最快的修法:对确实已经不存在的 URL,让服务端返回 404(或 410)而不是 200;对那些本该有内容、却在 Googlebot 眼里是空的真实页面,用 URL Inspection → Test Live URL 看渲染后的 HTML,把缺的东西补上。
这几乎从来不是内容质量处罚,而是状态码或渲染的问题——要么页面本不该返回 200,要么它返回了 200 但在 Googlebot 那里渲染成了空白。
先判断:你属于哪一类
修法完全取决于这个 URL 是否应该存在。对一个被标记的 URL 跑 URL Inspection,点 Test Live URL,再展开 View Tested Page,对照下表判断:
| 你看到的情况 | 类别 | 修法 |
|---|---|---|
| URL 对应的内容已删除 / 从未存在 | 应该是真 404 | 返回 404 或 410(Step 2-3) |
| 空 tag / category / 0 结果搜索页 | 单薄的索引页 | noindex 或 404(Step 4) |
| 渲染后的 HTML 里你的真实内容都在 | 单薄页面被误判 | 补充实质内容后再验证 |
| 渲染后的 HTML / 截图是空白或报错 | JS 渲染失败 | 修渲染(Step 5) |
任意乱填的 URL 都返回首页或 index.html | SPA / 重定向配错 | 修分发层(Step 6) |
View Tested Page 面板有三个标签页——HTML、Screenshot、More Info。Screenshot 标签显示 Googlebot 实际渲染出来的样子;More Info → JavaScript console messages 会列出 Googlebot 在渲染过程中遇到的所有 console 报错。hydration 时一个未捕获的异常就足以让你的主内容整块消失。(注意:Test Live URL 这种实时测试不会更新索引——它只是预览 Google 此刻抓取会看到什么。)
常见原因
1. 空的 category / tag / 搜索结果页返回 200
最高频。比如 /tag/unused-tag/ 没有任何文章,但模板照样渲染了页眉页脚 + “暂无内容”——HTTP 200,但 body 几乎是空的。
如何判断:
curl -sI "https://yourdomain.com/tag/empty-tag/" | head -1
# 期望 404;实际是 HTTP/2 200 就是 Soft 404 候选
2. 动态页面找不到内容时仍返回 200
// 错误实现
app.get('/article/:slug', async (req, res) => {
const article = await db.findOne({ slug: req.params.slug });
if (!article) {
res.render('not-found', { message: 'Article not found' }); // 200
return;
}
res.render('article', { article });
});
res.render('not-found') 默认返回 200。应该改成 res.status(404).render('not-found')。
3. SPA 的 404 路由返回 index.html + 200
如果你部署到 Firebase / Netlify / Vercel 并用了 SPA fallback:
{
"rewrites": [
{ "source": "**", "destination": "/index.html" }
]
}
所有 URL 都返回 index.html + 200,包括根本不存在的 URL。然后前端 router 显示 “404 Not Found”——但 HTTP 状态码仍然是 200。
4. 用重定向到首页代替 404
// 错
app.get('*', (req, res) => res.redirect('/'));
URL 不存在时不该悄悄重定向到首页。Google 会抓到一堆”URL 各不相同、内容却都是首页”的页面,于是标 Soft 404 或 Duplicate。
5. 只有导航 / 页脚 / 广告,没有主内容
页面 HTML 加载完,body 里有 header、nav、sidebar、ads、footer——但 <main> 里只有”加载中”或者干脆是空的。
# 看 main 区域的字符数
curl -sL https://yourdomain.com/page | grep -oE '<main[^>]*>[\s\S]*</main>' | wc -c
# 不到 500 个字符通常就是 Soft 404 候选
6. 在你浏览器里渲染正常,但对 Googlebot 是空白
如果你的站点靠客户端渲染或 SSR,而渲染过程出错(或某个关键脚本没加载成功),即便页面在你的 Chrome 里好好的,Googlebot 看到的也是一张空白页。这一类最容易让人困惑:URL 在浏览器里正常,但 Test Live URL 的截图是空的。去 More Info → JavaScript console messages 里找那个挂掉的脚本。
最短修复路径
Step 1:导出 Soft 404 清单并分类
Search Console → Indexing → Pages(页面) → 在 “Why pages aren’t indexed” 下点 Soft 404 → 导出 URL 列表。按 URL 模式分类:
/tag/* → 空 tag 页
/search?q=* → 0 结果搜索页
/article/* → 内容已删但 URL 还在
/products/* → 库存为 0 的商品
每一类的修法都不一样。
Step 2:把”该 404”的情况改成真 404
// Express / Node
app.get('/article/:slug', async (req, res) => {
const article = await db.findOne({ slug: req.params.slug });
if (!article) {
return res.status(404).render('not-found', { message: 'Article not found' });
}
res.render('article', { article });
});
// Next.js App Router
import { notFound } from 'next/navigation';
export default async function ArticlePage({ params }) {
const article = await getArticle(params.slug);
if (!article) notFound(); // 渲染 not-found 界面并带 404 状态码
return <Article {...article} />;
}
// Astro
---
const article = await getArticle(Astro.params.slug);
if (!article) return new Response(null, { status: 404 });
---
给 404 页面放一点真正有用的内容(一个搜索框、几个热门页面的链接)。一个光秃秃的”Not found” + 200 状态码就是教科书式的 soft 404;而一个内容有用、状态码正确的 404 页面则完全没问题。
Step 3:永久删除的 URL 用 410 Gone(意图更清晰)
如果某个 URL 是永久删除而不是”暂时不在”,返回 410:
res.status(410).send('This page has been permanently removed.');
关于那句流传很广的”410 比 404 去索引更快”:Google 官方文档明确说,爬虫对 404 和 410 的处理是一样的——两者都告诉索引流程内容已经没了、都会从索引里移除,抓取频率也都会逐渐降低。所以 410 并不能可靠地加快什么,它真正的价值在于明确传达意图(“彻底没了”,而不是”过会儿再试”)。两种状态码都能解决 soft 404——关键是你别再返回 200。
Step 4:空 tag / 0 结果搜索页加 noindex 或 404
// 空 tag 页:noindex + 不进 sitemap
export default function TagPage({ posts }) {
if (posts.length === 0) {
return (
<>
<Helmet><meta name="robots" content="noindex,follow" /></Helmet>
<main>这个标签下暂无文章</main>
</>
);
}
// ...
}
或者更激进:直接 404,因为空 tag 页对用户其实也没价值。注意:如果你想让这些页面被去索引,不要在 robots.txt 里屏蔽它们——被屏蔽的 URL 不会被抓取,Google 也就永远看不到那个 noindex 标签。让 Google 抓取它们、读到 noindex 才行。
Step 5:针对”JS 空白”那一类修渲染
如果某个本该有内容的页面,Test Live URL 的截图却是空白:
- 打开 More Info → JavaScript console messages,修掉列表里的第一个报错(通常是 hydration 失败,或某个关键 bundle 返回了 4xx/5xx)。
- 确认没有关键 JS/CSS 文件被
robots.txt屏蔽——Googlebot 需要抓到它们才能渲染。 - 对于必须被收录的内容,优先用 SSR / 静态渲染而不是纯客户端渲染,这样 HTML 响应里就已经带着内容了(可以用
curl -sL <url> | grep搜一个已知短语来确认)。 - 修好后重新跑一次 Test Live URL,确认渲染后的 HTML 里现在有你的内容了。
Step 6:SPA 部署——在分发层修
如果你用的是 Firebase Hosting / Netlify / Vercel 的 SPA fallback:
// firebase.json
{
"hosting": {
"rewrites": [
{ "source": "/article/**", "function": "ssrArticle" },
{ "source": "**", "destination": "/index.html" }
]
}
}
对 /article/** 这种需要真 404 的路径走 server function,让它能返回 404;其余真正的客户端路由再保留 SPA 的 200 fallback。
对于纯客户端渲染(CSR)的站点,你无法从客户端改 HTTP 状态码——服务器在你的 router 跑起来之前就已经发出了 200。服务端渲染(一个 SSR 函数或预渲染)是唯一能发出真 404 的办法。
Step 7:用 curl 验证 + Search Console 重新验证
# 应该 404 的 URL
curl -sI "https://yourdomain.com/article/already-deleted" | head -1
# 期望:HTTP/2 404
# 应该 200 的 URL
curl -sI "https://yourdomain.com/article/exists" | head -1
# 期望:HTTP/2 200
然后在 Search Console → Indexing → Pages → Soft 404 那一行点 Validate Fix(验证修复)。这会启动一个跟踪周期(在 “See details” 里能看到状态从 Started 变成 Passed 或 Failed),完成后 Google 会发邮件通知你。截至 2026 年 6 月,一次完整复审通常需要约 1-3 周;不用反复点——再点一次只会让计时重新开始。
怎么确认修好了
- 对原先被标记的 URL 跑
curl -sI,返回的是你想要的状态码(已删页面是404/410,真实页面是200)。 - 对一个真实页面跑 URL Inspection → Test Live URL,渲染后的 HTML 和截图里能看到你的内容——没有空白或报错画面。
- Search Console 里的 Soft 404 验证走到 Passed,接下来几周 Pages 报告里受影响的数量下降。
预防建议
- 任何”找不到资源”的代码路径都必须
res.status(404)(或410),不要默认200。 - 给 404 页面放上真正有用的内容(搜索框、热门链接);光秃秃的”Not found” + 200 就是典型的 soft 404。
- 空的 category / tag / 搜索结果页要么
noindex要么404,不要返回200的空壳页;想去索引的页面也别用robots.txt屏蔽。 - SPA 部署必须区分”前端的 404 界面”和”服务端 404”,后者要靠 SSR 函数返回真
404。 - CI 加一个 smoke test:随机访问一个不存在的 URL,断言状态码是
404而不是200。
常见问题
Soft 404 会影响我的排名吗? 不会直接影响——被标记的 URL 只是不会被收录,所以根本排不上。它更像一个信号:这些 URL 在浪费抓取预算,而且可能意味着有更大范围的状态码 bug。把状态码改对之后,这些 URL 要么被收录(如果是真实页面),要么被干净地移除(如果已经没了)。
页面在我浏览器里好好的,为什么 Google 说它是 Soft 404? Googlebot 用它自己的无头 Chromium 渲染,对报错更严格。跑 URL Inspection → Test Live URL 看截图:如果是空白,说明某个脚本在渲染时挂了。去 More Info → JavaScript console messages 找原因。浏览器与 Googlebot 的差异,几乎都归结为渲染报错或某个资源被屏蔽。
已删页面该用 404 还是 410?
两者都能解决 soft 404。Google 在索引层面对二者一视同仁,所以想明确表示”页面彻底没了”就用 410,其他情况用 404。关键是别再返回 200。
“Validate Fix” 要多久? 一个跟踪周期,截至 2026 年 6 月通常约 1-3 周。完成后 Google 会发邮件通知你。在它跑完之前再点一次 Validate Fix 只会重新计时——让它自己跑完就好。
纯客户端渲染(CSR)的站点能返回真 404 吗?
不能。服务器在你的 JavaScript router 跑起来之前就已经返回了 200,状态码早就定了。你需要 SSR、预渲染,或者一个能为”未找到”路径返回 404/410 的服务端 / 边缘函数。