文章指出,作者网站因站内搜索被恶意刷出大量垃圾URL,导致全站收录断崖下跌。作者分析了robots.txt拦截、noindex拦截和Nginx防御的误区,提出正确顺序是先清理索引库,再封堵抓取通道,并建议在HTTP响应头注入X-Robots-Tag来更有效地阻止页面收录。
上周一早晨打开百度搜索资源平台和 Google Search Console,我差点以为自己网站被黑客爆破了:索引量在过去四天里莫名其妙从原本的 4000 多页直接飙到了 23 万页。
但与收录暴涨相反,自然搜索流量不仅没有涨,反而腰斩了 60% 多。更要命的是,云服务器的 CPU 监控报警响个不停,MySQL 的连接池直接被打满,慢查询日志里全是 LIKE '%...%' 的全表扫描。
登录服务器抓取 Nginx 访问日志看了一眼,满屏全是一串串带着生僻博彩、违禁词的查询请求:
114.238.xx.xx - - [12/May/2026:04:15:22 +0800] "GET /search?keyword=%E6%BE%B3%E9%97%A8%E6%96%B0%E8%91%A1%E4%BA%AC... HTTP/1.1" 200 15421 "-" "Mozilla/5.0..."
157.55.39.xx - - [12/May/2026:04:15:23 +0800] "GET /search?keyword=%E7%9A%87%E5%86%A0%E4%BD%93%E8%82%B2... HTTP/1.1" 200 15302 "-" "Baiduspider+(+http://www.baidu.com/search/spider.htm)"
黑产脚本在批量往站内搜索接口灌词,而各大搜索引擎的爬虫正顺着这些 URL 疯狂抓取,把成千上万个根本没有有效内容的搜索结果页抓进了索引库。
这就是典型的“搜索型蜘蛛陷阱”。正常内容还没被蜘蛛爬完,站点的抓取配额(Crawl Budget)就已经被这几十万个垃圾页面挥霍一空,甚至触发了搜索引擎针对低质垃圾页(Thin Content)的算法降权。
整整排查和治理了三天,我把这里面踩过的坑和最终的防御策略整理出来。
误区一:第一反应去改 robots.txt,结果垃圾快照根本下不掉
很多人发现搜索接口被爬,第一反应就是在根目录的 robots.txt 里补一行:
User-agent: *
Disallow: /search
Disallow: /search?*
我也这么做了。在 Nginx 刷新后,抓取日志里的爬虫请求确实断崖式下降。但三天过去,搜索引擎里的垃圾收录一条都没少。
甚至在 Google 里搜索这些违禁词时,搜出来的结果依然挂着我网站的域名,只是快照描述变成了那行灰色的提示:“由于该网站的 robots.txt,无法显示此网页的说明”。
为什么?
因为 robots.txt 约束的是“爬取行为”(Crawling),而不是“索引行为”(Indexing)。
如果一个 URL 已经被蜘蛛发现、或者在外部被黑产挂了外链,搜索引擎即便被 robots.txt 挡在门外不再抓取页面 HTML,它依然会把这个 URL 保留在索引库里。
更严重的是,因为你在 robots.txt 里把爬虫彻底拦死了,蜘蛛每次来访看到 Disallow 就会掉头走人,根本没有机会重新发起 HTTP 请求去读取你后续配置的 noindex 标签或者 404/410 状态码。这些垃圾页面反而被永久“冰冻”在快照库里,半个月都刷不掉。
正确的顺序是:治理初期千万不要直接在 robots.txt 里一刀切封死。必须放爬虫进来拿到明确的注销信号,等索引库里的垃圾快照彻底清理得差不多了,再去封堵抓取通道。
误区二:只在 HTML 模板里加 meta noindex,爬虫却当耳旁风
既然不能直接封 robots.txt,那就在搜索结果页加 noindex。
我一开始在站内搜索页的 Blade/PHP 模板头部加了这一段:
<meta name="robots" content="noindex, nofollow">
理论上蜘蛛抓到这个 HTML,读到 <head> 里的标签就会把页面从收录里剔除。
但实际跑起来有两个致命盲区:
第一,很多内容系统在关键词无搜索结果时,会返回统一的空白页或者错误页,一些前端框架(尤其是 SPA 客户端渲染或者异步抓取接口)根本不会渲染这个 meta 标签,甚至有些精简版抓取蜘蛛在解析超大 HTML 时遇到语法截断,直接跳过了 head 内的指令。
第二,搜索引擎为了解析 meta 标签,必须把整个页面从服务器下载下来并跑一遍 DOM 解析,这在数万并发抓取时对源站服务器依然是巨大负担。
彻底解决这个问题的标准做法是在 HTTP 响应头里注入 X-Robots-Tag。
直接在 Nginx 层面或者后端中间件加这一行:
# 针对站内搜索的 location 块,强制返回无索引头
location /search {
add_header X-Robots-Tag "noindex, nofollow" always;
# 代理至后端应用服务
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
HTTP 报文头部返回的数据是爬虫最先接触到的协议字段:
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 14 May 2026 08:30:12 GMT
Content-Type: text/html; charset=UTF-8
X-Robots-Tag: noindex, nofollow
无论是抓取 HTML、JSON 还是 API 接口,所有搜索引擎爬虫只要读到这个 HTTP 响应头,就会在不浪费渲染算力的情况下,把该 URL 从待收录池中剔除。
误区三:搜索无结果依然返回 200,Canonical 标签指向自身
这是导致搜索引擎将站点判定为“作弊农场”的核心原因。
排查模板代码时我发现两个非常离谱的默认逻辑:
- 软 404(Soft 404)现象:黑产注入了一长串火星文或者非法字符,后台查不到任何文章,但页面依然返回了
HTTP/2 200 OK,正文展示“抱歉,没有找到相关内容”,周围却保留了全站统一的导航栏、推荐文章和最新动态。在百度和 Google 算法眼里,几十万个 URL 内容几乎完全一样(全站公共模板),这就是最恶劣的重复低质页。 - Canonical 错配:页面 head 标签里居然自动生成了这一行:
<link rel="canonical" href="https://www.llbbs.cn/search?keyword=非法词" />Canonical 的作用是告诉搜索引擎“这个 URL 是规范权威版本”。系统把垃圾参数当成了权威 URL,等于是在主动向爬虫担保这个垃圾页面具有独立价值。
针对搜索结果页的 URL 规范化处理规则必须严格限制:
- 搜索结果页绝对禁止输出指向自身的 canonical 标签。宁可留空,也决不能自引用。
- 非法参数直接阻断:只要包含非法编码、特殊字符或长度异常的查询词,后端直接返回
HTTP 400 Bad Request,不进入业务查询逻辑。
在 PHP / Node 后端中可以做个前置校验:
// 限制搜索词长度与合法字符
$keyword = trim($_GET['keyword'] ?? '');
// 超过 20 个字符,或者包含特殊注入特征,直接 400 截断
if (mb_strlen($keyword) > 20 || preg_match('/[<>{}\[\]\\/\\^`~]/', $keyword)) {
http_response_code(400);
header('X-Robots-Tag: noindex, nofollow');
exit('Invalid search query.');
}
// 正常搜索结果,必须输出 noindex 响应头
header('X-Robots-Tag: noindex, nofollow');
误区四:前端组件的“热门搜索”把蜘蛛直接往陷阱里带
排查到第三天,我一直想不明白一个关键点:黑产虽然在调我的接口,但搜索引擎是怎么知道那二十几万个带参 URL 的?
爬虫不可能凭空猜出黑产刷过哪些特定参数。
最后翻看前端模板代码,终于抓到了内鬼:首页侧边栏有一个“最近搜索”和“大家都在搜”的小模块。
最初为了提高用户体验,把数据库里最近查询过的 10 条记录存入 Redis,然后在首页直接循环输出成了超链接:
<!-- 致命的内链漏洞:为爬虫铺平抓取道路 -->
<div class="hot-search">
<span>大家都在搜:</span>
<a href="/search?keyword=澳门新葡京...">澳门新葡京...</a>
<a href="/search?keyword=皇冠体育...">皇冠体育...</a>
</div>
黑产脚本只要发起一次请求,这个词就会被缓存并展示在首页。
而首页是整个网站权重最高、爬虫光顾最频繁的入口。蜘蛛一爬首页,顺着这些 <a> 标签一一点进去,瞬间就发现了新的搜索结果页,并在接下来的抓取周期里不断扩散,彻底形成死循环。
这个漏洞的治理非常直接:
- 彻底下线全自动无审核的“最近搜索”超链接。
- 如果一定要放推荐搜索词,必须在后台通过运营白名单维护,坚决不直接读取前台用户的输入历史。
- 纯用户个人的搜索历史记录,改用浏览器端
localStorage本地存储与渲染,绝不在服务端吐出的静态 HTML 源码里留下可供爬虫跟踪的href链接。
Nginx 层的最终防御与限流配置
修复了代码逻辑和响应头后,最后一步是在服务器入口把黑产的滥用请求挡在门外,避免 MySQL 被慢查询拖死。
在 Nginx 的主配置中添加针对搜索接口的独立限流和特征过滤:
# 1. 定义按客户端 IP 限流的区域,每分钟最多 10 次请求
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=10r/m;
server {
server_name www.llbbs.cn;
# 2. 针对站内搜索路由进行精准拦截与治理
location = /search {
# 强制下发无索引指令,兜底所有爬虫
add_header X-Robots-Tag "noindex, nofollow" always;
# 检查 keyword 参数长度:如果查询词超过 30 个字符,直接 403 丢弃
if ($arg_keyword ~ ".{31,}") {
return 403 "Search query too long";
}
# 拦截常见的高危黑产特征关键词(根据日志提炼)
if ($arg_keyword ~* "(bet|casino|slot|porn|六合彩|代开|发票|博彩)") {
return 403 "Forbidden query";
}
# 开启频次限制,允许 5 个突发并发
limit_req zone=search_limit burst=5 nodelay;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
配置生效之后,Nginx 层面直接拦截了 98% 以上的恶意探测,后端数据库 CPU 占用率从 95% 瞬间降回到了 5% 以内。
经过两周的爬虫回访与重抓,Google Search Console 和百度站长平台的索引曲线终于开始呈断崖式回落,原本被挤占抓取配额的正常技术文章也重新恢复了日常抓取与排名展示。
对于任何内容类或博客网站,站内搜索页本来就是给真实用户找文章用的,它既不具备独立的长尾词价值,又极容易成为攻击者污染域名的通道。把它从第一天起就彻底与搜索引擎隔离,才是保证整体 SEO 权重健康的最稳妥策略。
文章标题:站内搜索被恶意刷出几十万个垃圾 URL 导致全站收录断崖下跌?排查爬虫蜘蛛陷阱、noindex 拦截与 Nginx 防御我踩过的几个坑
文章链接:https://www.llbbs.cn/jishujaocheng/253.html
本站文章均为原创,未经授权请勿用于任何商业用途
评论一下?