侧边栏壁纸
  • 累计撰写 158 篇文章
  • 累计收到 2 条评论

新发文章搜索引擎死活不收录?排查 Nginx 爬虫误拦、斜杠死循环与抓取配额浪费我踩过的几个坑

2026-10-4 / 0 评论 / 12 阅读
AI 摘要由 AI 生成

新文章不收录可能因Nginx配置错误导致。文章通过分析Nginx日志,发现爬虫被误拦、死循环或抓取配额浪费。作者提出解决方案,如单开蜘蛛日志、标记蜘蛛User-Agent等,以排查并修复这些问题。

前阵子我给几个站点加了新栏目,连着更新了十几篇长文,主动把链接提交到百度搜索资源平台和 Google Search Console。按理说站点已经稳定跑了一年多,新文章少说也该在一两天内进库。结果等了整整两周,后台的索引量曲线纹丝不动。去站长平台看抓取频次,每天只有零星几次,甚至还有大量抓取失败的报警。

很多人遇到不收录,第一反应往往是怀疑文章质量不够高、原创度低或者域名被进了沙盒。但当我连上服务器翻开 Nginx 的原始访问日志时,才发现问题根本没出在内容上:真实的搜索引擎蜘蛛确实来过,但要么被误配的安全规则挡在门外,要么被复杂的重定向绕进死胡同,要么把有限的抓取额度全耗在垃圾参数页上。

把这套服务器层面的排查过程整理出来,梳理几个最隐蔽的技术 SEO 坑,以及具体的日志定位方法与修复规则。

准备工作:在 Nginx 中单开蜘蛛专属日志

排查爬虫问题,第一步是得看清蜘蛛到底有没有来、抓了什么、拿到了什么状态码。

默认的 Nginx access.log 混杂了普通用户访问、静态资源请求和各种扫描脚本,直接用 grep 搜爬虫既慢又容易漏。在主配置里给各大主流搜索引擎蜘蛛打上标记,并单独输出到独立的蜘蛛日志文件,排查效率会高很多。

在 nginx.conf 的 http 块中加入基于 User-Agent 的变量映射与日志格式定义:

map $http_user_agent $is_spider {
    default 0;
    ~*(Baiduspider|Googlebot|bingbot|Sogou\ web\ spider|YisouSpider) 1;
}

log_format spider_log '$remote_addr - [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" $request_time $upstream_response_time';

然后在对应的 server 块中,设置只记录命中爬虫的请求:

access_log /var/log/nginx/spider_access.log spider_log if=$is_spider;

重载 Nginx 之后,只要有合法的蜘蛛访问,就会实时写入 spider_access.log。排查时只要用一条简单的 awk 命令,就能看到过去 24 小时各大蜘蛛的状态码分布:

awk '{print $9, $1}' /var/log/nginx/spider_access.log | sort | uniq -c | sort -nr

正常情况下,状态码应该以 200 为主,辅以少量的 304 或合规的 301。如果看到大面积的 403、502 或者连续跳转,说明抓取环节已经出问题了。

坑一:防扫描与 WAF 规则把真实蜘蛛拒之门外

现在的服务器天天被各种自动化脚本扫描,很多站长习惯在 Nginx 里配置限流模块(limit_req)或者封禁 User-Agent 的黑名单。

我当初为了防御某个恶意刷接口的脚本,在全局配置里加了一段针对高频请求的速率限制,限制每个客户端 IP 每秒最多只能发起 2 个请求:

limit_req_zone $binary_remote_addr zone=req_limit:10m rate=2r/s;

平时普通用户浏览网页完全不受影响,但搜索引擎爬虫的工作模式完全不同。像百度蜘蛛或者 Googlebot 爬取整站结构时,往往会在几秒钟内高并发地发出几十个探测请求。当抓取频率超过这个阈值时,Nginx 就会给蜘蛛返回 503 Service Temporarily Unavailable。

不仅是限流,更常见的是 User-Agent 正则误杀。比如有些防采集规则写成这样:

if ($http_user_agent ~* (python|curl|bot|spider|crawler)) {
    return 403;
}

本意是想拦未授权的爬虫,结果把包含了 Googlebot、bingbot 的合规搜索引擎蜘蛛全部一并干掉了,爬虫每次来访问新文章直接吃 403 Forbidden。

有些站长知道伪造 User-Agent 很简单,于是不敢直接放行。其实判断是不是真正的百度或谷歌蜘蛛,不能单凭客户端带的 User-Agent 字符串,必须通过 IP 反向 DNS 解析(rDNS)来确认。

在 Linux 终端用 host 或 dig 命令对来访 IP 做 PTR 反查:

host 220.181.108.77
# 输出:77.108.181.220.in-addr.arpa domain name pointer baiduspider-220-181-108-77.crawl.baidu.com.

host baiduspider-220-181-108-77.crawl.baidu.com
# 正向解析必须解析回原始 IP:220.181.108.77

确认是正规爬虫的 IP 段后,如果要在 Nginx 里做限流,需要使用 geo 模块将合规蜘蛛的网段从限流池中剥离出来:

geo $white_spider {
    default 0;
    116.179.32.0/24 1; # 百度常用抓取段示例
    220.181.108.0/24 1;
    66.249.64.0/19 1;  # Googlebot IP 段
}

map $white_spider $limit_key {
    0 $binary_remote_addr;
    1 ""; # 命中白名单时 key 为空,Nginx 自动跳过限流
}

limit_req_zone $limit_key zone=req_limit:10m rate=2r/s;

这样既保住了抗攻击能力,又避免了蜘蛛爬到一半被频繁 503 拦截。

坑二:尾部斜杠与多层 Rewrite 造成的重定向损耗

第二个非常隐蔽的坑是 URL 规范化配置不严谨带来的连环 301 重定向。

现代搜索引擎对每个网站分配的“抓取配额(Crawl Budget)”是有限的。如果蜘蛛发现抓取一个 URL 需要经历多层跳转,不仅消耗配额,还可能因为重定向次数过多判定该链接不稳定。

常见的死穴是这样的链条:
用户或外链输入:http://example.com/blog/article-1

  1. 第一跳:Nginx 的 HTTP 强制跳转 HTTPS(跳到 https://example.com/blog/article-1,301)
  2. 第二跳:Nginx 缺少非 www 到 www 的归一化,再次跳转(跳到 https://www.example.com/blog/article-1,301)
  3. 第三跳:CMS 或前端路由强制要求末尾带斜杠,再次跳转(跳到 https://www.example.com/blog/article-1/,301)

一个简单的页面抓取,连续发生了 3 次 301。更糟糕的是如果开发人员在博客模板内部的 canonical 标签里写的又是不带斜杠的链接,就会导致蜘蛛在两个地址之间反复横跳,收录迟迟无法落地。

用一条 curl 命令就能看清真实的跳转链条:

curl -ILs "http://example.com/blog/article-1" | grep -E "HTTP/|Location:"

在 Nginx 接入层实现“一步到位”跳转,把协议、主域名和斜杠规则合并在同一个 server 块或第一条 rewrite 规则中直接处理完毕:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

针对内容详情页的 URL 形式,在项目一开始就要统一决定是否带斜杠或 .html 后缀。确定后,在页面 HTML <head> 区域必须输出完全一致的 Canonical 标签:

<link rel="canonical" href="https://www.example.com/blog/article-1.html" />

明确告诉搜索引擎哪一个是标准版本,避免权重分散与无谓的二次重定向。

坑三:动态过滤参数吃光了有限的抓取预算

如果打开 spider_access.log,发现蜘蛛每天抓了几千次,但新发布的文章列表依然毫无动静,很可能是爬虫陷入了站内的参数黑洞。

很多独立博客或者内容系统自带有标签页、归档页、站内搜索和排序功能。常见的 URL 形如:
/tag/linux?sort=desc&page=1
/search?keyword=nginx
/post/123?replytocom=45

这些链接对普通访客查阅历史内容很有帮助,但对爬虫来说,只要页面上有排序选项、分页组合或者评论回复锚点,排列组合出来的 URL 数量就是成千上万个。蜘蛛进入网站后,会把大量的抓取配额全部浪费在爬取这些重复内容或空列表上,真正挂在首页和最新发布列表里的文章,反而因为抓取队列排满而轮不上。

排查日志里是否有被参数带偏的情况:

awk -F'"' '{print $2}' /var/log/nginx/spider_access.log | awk '{print $2}' | grep '?' | cut -d'?' -f1 | sort | uniq -c | sort -nr | head -n 10

如果统计出来有大量的带参路径被高频访问,必须在 robots.txt 中加以约束。

一份清晰健康的 robots.txt 配置范例:

User-agent: *
Disallow: /admin/
Disallow: /search
Disallow: /*?*sort=
Disallow: /*?*order=
Disallow: /*?*replytocom=

Sitemap: https://www.example.com/sitemap.xml

对于那些确实需要保留给用户、但不希望搜索引擎建索引的筛选聚合页面,可以在 Nginx 层面通过响应头告知蜘蛛无需索引:

location /search {
    add_header X-Robots-Tag "noindex, follow" always;
    # 正常代理或处理逻辑
}

让爬虫抓取后直接标记为不建立索引,但继续跟踪页面内的链接,把宝贵的抓取权重引流回具体的正文页。

坑四:动静缓存与协商缓存失控导致的 304 与空白抓取

第四个坑主要出现在配置了反向代理缓存(如 Nginx FastCGI Cache / Proxy Cache)或者使用了单页应用(SPA)的项目中。

第一种情况是 ETag 和 Last-Modified 头配置异常。有些运维为了压榨服务器性能,给所有动态文章也配置了长周期的静态缓存策略,或者 FastCGI 缓存没有在后台发布新文章时及时清理(purge)。蜘蛛带着 If-Modified-Since 发送请求时,服务器一直无条件返回 304 Not Modified,导致搜索引擎判定页面没有任何新内容更新,放弃更新索引快照。

第二种情况更为严重:前后端分离项目直接返回没有正文的空 HTML 壳子。

像 Vue 或 React 搭建的站点,如果直接部署在静态服务器上,Nginx 返回的原始 HTML 往往只有这一段:

<div id="app"></div>
<script src="/js/app.bundle.js"></script>

虽然现代 Googlebot 具备一定的 JavaScript 渲染能力,但国内的百度、搜狗等爬虫对客户端渲染的支持度依然极低。蜘蛛下载 HTML 时正文内容为空,就会被判定为无效的低质空页面,甚至直接丢弃。

检查蜘蛛眼中的实际页面输出非常简单,在终端用 curl 模拟爬虫 UA 抓取:

curl -s -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" https://www.example.com/article/123.html | grep -i "div id=\"app\""

如果 curl 抓出来的 HTML 里搜不到文章正文标题和文字,说明必须要做服务端渲染(SSR)或者预渲染(Prerender)。

对于不想重构现有单页架构的项目,可以在 Nginx 层面结合轻量预渲染服务(如 Rendertron 或 prerender.io),针对爬虫做区分转发:

map $http_user_agent $prerender_bypass {
    default 0;
    ~*(Baiduspider|Googlebot|bingbot|Sogou) 1;
}

location / {
    if ($prerender_bypass = 1) {
        proxy_pass http://127.0.0.1:3000/render?url=https://$host$request_uri;
        break;
    }
    try_files $uri $uri/ /index.html;
}

只有当探测到来访者是搜索引擎爬虫时,才把请求交给无头浏览器渲染成完整的 HTML 字符串返回,确保蜘蛛拿到的每一次快照都是饱满的有效文本。

建立日常监控检查项

把这四个环节全部梳理一遍之后,两周未收录的问题在两三天内就得到了解决:蜘蛛日志里的状态码稳定在 200,抓取频次恢复正常,站长平台的有效索引量开始平稳上升。

服务器层面的技术 SEO 不需要每天盯着看,但几个基本环节必须理顺:

  1. 用独立的 log 记录合规蜘蛛抓取,定期看一眼状态码分布。
  2. 限流和防刷规则必须对正规爬虫网段做白名单豁免,严防 403 和 503 误伤。
  3. 规范化整站的重定向逻辑,保证协议、域名和路径一次性 301 落地。
  4. 在 robots.txt 中卡住无意义的动态参数,将抓取预算留给真正的内容页面。
  5. 用 curl 模拟爬虫检查输出内容,确认返回的 HTML 带有完整的正文文本。

先把通道打通,让爬虫能无障碍、低成本地读到正文,优质的内容才有机会在搜索结果里拿到该有的排名。

评论一下?

OωO
取消