本文约 3,530 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:几万条 ?s= 页面混进了索引库
打开 Search Console 的”网页”报告,有时会发现索引量比实际文章数多出好几倍,点开一看全是 /?s=关键词 或 /search/关键词 这样的 URL。这些页面内容是由用户或爬虫任意组合关键词生成的,标题千奇百怪,正文可能是”未找到结果”,也可能混杂了几十条摘要片段。它们既没有搜索价值,又会消耗大量抓取预算,让真正重要的新文章迟迟不被抓取。
Google 官方对这类页面的态度很明确:站内搜索结果页不应被索引。官方帮助文档给出的建议是对搜索结果页使用 noindex,而不是放任其被收录。问题在于,WordPress 默认并不会给搜索页加 noindex,很多主题也没处理,于是这些页面就顺理成章地进了索引库。
原理分析:为什么 noindex 优于 robots.txt 屏蔽
很多人的第一反应是在 robots.txt 里写 Disallow: /?s=。这有两个隐患:第一,robots.txt 只阻止抓取,不阻止索引——如果这些 URL 被外部链接指向,Google 仍可能在没有内容的情况下把它们编入索引,形成”无内容的索引页”;第二,Disallow 会让 Google 无法看到页面里的 noindex 指令,指令永远不生效。
| 方案 | 能否抓取 | 能否索引 | 适用 |
|---|---|---|---|
| 什么都不做 | 能 | 能 | 不推荐 |
| robots.txt Disallow | 否 | 可能索引 | 仅配合用 |
| noindex 响应头/标签 | 能 | 否 | 推荐 |
| noindex + Disallow | 否 | 否(指令可能读不到) | 需谨慎 |
正确做法是允许抓取、输出 noindex,让 Google 抓到页面后读到指令并放弃索引;确认索引清除后,再考虑是否用 robots.txt 减少抓取浪费。
另外要区分两类搜索页:一是 WordPress 原生的 /?s=;二是主题或插件(如 WooCommerce 的产品搜索、FacetWP、Search & Filter)生成的筛选页 /?color=red&size=m。筛选参数页的体量往往比原生搜索页大得多,是更需要处理的重灾区。参数页面建议结合 canonical 指向无参数版本一并处理。
保姆级实操
步骤 1:确认当前状态。 在 GSC 里搜索 site:你的域名/?s=(或用”网页”报告筛选含 s= 的 URL),统计已索引数量;再打开一个搜索页查看源码,确认有没有 <meta name="robots" content="noindex">。
步骤 2:用 wp_robots 过滤器输出 noindex。 WordPress 5.7 起内置 wp_robots() 输出 robots 元标签,通过 wp_robots 过滤器追加指令即可,无需手写 wp_head。
<?php
// 搜索结果页输出 noindex、nofollow(WordPress 5.7+)
add_filter('wp_robots', function ($robots) {
if (is_search()) {
$robots['noindex'] = true;
$robots['nofollow'] = true;
$robots['noarchive'] = true;
}
return $robots;
});
若站点还在用旧版本 WordPress,可在 wp_head 里手工输出:
<?php
// 兼容旧版本:直接输出 meta 标签
add_action('wp_head', function () {
if (is_search()) {
echo '<meta name="robots" content="noindex,nofollow,noarchive">' . "\n";
}
}, 1);
步骤 3:处理带参数的筛选页。 对指定参数组合输出 noindex,同时保留 canonical 指向无参数版本。
<?php
// 对带筛选参数的归档页输出 noindex,并把 canonical 指向干净 URL
add_filter('wp_robots', function ($robots) {
$params = ['color', 'size', 'brand', 'min_price', 'max_price'];
foreach ($params as $p) {
if (isset($_GET[$p])) {
$robots['noindex'] = true;
break;
}
}
return $robots;
});
add_filter('wpseo_canonical', function ($canonical) {
if (is_post_type_archive('product') && !empty($_GET)) {
return get_post_type_archive_link('product'); // 指向无参数版本
}
return $canonical;
});
步骤 4:确认索引已清除后再考虑 robots.txt。 只有在 GSC 确认这些 URL 已全部移出索引后,才适合加抓取限制,避免指令读不到。
# robots.txt —— 仅在 noindex 生效一段时间后再考虑添加
User-agent: *
Disallow: /?s=
Disallow: /search
Allow: /
注意 Disallow: /?s= 对路径形式 /search/xxx 无效,需按实际 URL 形态分别写。
步骤 5:从 sitemap 中排除。 Yoast SEO、Rank Math 默认都不会把搜索页写进 sitemap,但自定义 sitemap 时要确认没有包含搜索路由。
步骤 6:提交移除与复查。 在 GSC 的”移除”工具中对已索引的搜索 URL 提交临时移除,随后观察”网页”报告中的数量变化。搜索页与抓取预算的关系,可配合 Google 抓取预算优化 一起分析。
常见踩坑
- 案例: 某站只加了
Disallow: /?s=,没有输出 noindex。半年后 GSC 显示 1.2 万个?s=URL “已编入索引”,且因无法抓取而全部无标题无描述,占据索引报告的绝大多数。补上 noindex 并等四周后,索引数才逐步回落。 - 在
robots.txt屏蔽后又提交”移除 URL”,Google 读不到页面就无法执行移除请求,操作必然失败。顺序必须是先 noindex、后考虑 Disallow。 - 过滤器写成
is_search()却挂在wp_footer,输出位置不对导致 meta 标签出现在<body>中,指令失效。 - 只处理
/?s=,忽略插件生成的/?filter=xxx参数页,问题只解决了一半。 - 给搜索页加
noindex的同时又加了follow,爬虫顺着页面里的链接继续抓,浪费预算。搜索页应noindex,nofollow同时使用。 - 用
header()发送X-Robots-Tag却与插件输出的 meta 冲突,出现一处 noindex、一处 index 的矛盾,Google 会取最严格的那个,但排查会很混乱。 - 把搜索结果页做成”热门搜索聚合页”想蹭流量,内容却是自动生成的摘要堆叠,容易被判定为低质量门页。
总结
判断标准:任何由用户输入或参数组合动态生成的页面,只要内容没有人工编辑价值,就不应被索引。行动顺序是——先确认已索引数量;用 wp_robots 给 is_search() 和指定参数输出 noindex,nofollow;参数页补 canonical 指向干净 URL;确认索引清除后再考虑 robots.txt;最后用 GSC”移除”工具加速清理。做完这一步,你会发现抓取预算明显宽裕,新文章的收录速度也会变快。相关索引规则还可参考 robots.txt 配置教程。
