首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >robots.txt 拦得住 GPTBot 吗:AI 爬虫的约束边界、服务端兜底与日志验证

robots.txt 拦得住 GPTBot 吗:AI 爬虫的约束边界、服务端兜底与日志验证

原创
作者头像
用户10345225
发布于 2026-10-23 15:48:07
发布于 2026-10-23 15:48:07
460
举报

很多人配完 Disallow: / 就以为 AI 爬虫不来了,过一周翻日志发现 GPTBot 的请求一条没少。问题不在配置写错,而在于对 robots.txt 的能力边界有误解。这篇按「先看清现状 → 写规则 → 服务端兜底 → 日志验证」四步走,每一步都给能直接跑的命令。

一、robots.txt 的准确定位

它是一个放在域名根目录、路径固定为 /robots.txt 的纯文本文件。爬虫在抓取前会来读它,读到之后自行决定是否遵守。

这句话里有三个硬约束:

  1. 路径固定。只能放在根,放 /blog/robots.txt 无效,爬虫不会去读。
  2. 它是约定,不是访问控制。愿意读的爬虫按它办;不读的爬虫,它一点办法都没有。
  3. 它只管「抓不抓」,不管「索引不索引」。想让页面不被索引要用 noindex,但注意:如果你先用 Disallow 挡住抓取,爬虫就读不到页面里的 noindex,两件事会互相打架。

二、能约束什么:AI 爬虫是按 UA 分角色的

这是最容易踩坑的地方。同一个厂商往往有多个 UA,各自干不同的活,只拦一个等于只关了一扇门。

User-agent

用途

只拦它的后果

GPTBot

内容抓取

已抓走的内容不受影响,抓取频率下降但不会归零

OAI-SearchBot

检索与展示

页面可能不再出现在相关检索结果里

ChatGPT-User

用户提问时实时访问

用户主动询问时仍然会去抓你的页面

ClaudeBot

内容抓取

同上

PerplexityBot

检索索引

同上

CCBot

Common Crawl 采集

拦了只影响这一条上游,其他数据集照旧

所以第一件要决定的事不是「拦不拦」,而是「拦哪几个」。常见策略是:

  • 想保留 AI 检索带来的曝光:放行 OAI-SearchBot、PerplexityBot、ChatGPT-User,只拦纯训练抓取。
  • 电商站/内容站担心价格页、库存页被大量抓:对训练类 UA 做路径级限制,而不是整站 Disallow。

robots.txt 真正能做到的具体能力只有四项:

  • 路径级控制:Disallow: /checkout/ 这类。
  • UA 分组:不同 UA 给不同规则。
  • Sitemap 声明:这是引导,不是约束。
  • Crawl-delay:部分爬虫遵守,Google 明确不支持,其他家不保证,不要依赖它做限流。

三、管不住什么

这一节比上一节更重要,因为它决定了你要不要做服务端兜底。

  1. 不读 robots.txt 的爬虫。 协议是自愿的,不遵守的照样抓。
  2. 伪造 UA 的抓取。 换个普通浏览器 UA,你的 UA 规则全部失效。
  3. 已经抓走的内容。 robots.txt 没有追溯能力,改了规则不会让历史抓取回滚。
  4. 搜索引擎快照与第三方镜像。 你管得到自己的站,管不到别人复制走的那份。
  5. 通过 API、授权合作、数据集采购获得的内容。 这条链路根本不经过你的 robots.txt。
  6. 用户手动粘贴、浏览器插件、截图。 用户主动把内容送出去,服务端看不到。
  7. Disallow 挡不住「展示」。 页面被引用、被摘要,往往发生在检索层,不发生在抓取层。

结论:robots.txt 是「礼貌通知」,服务端才是「门禁」。两者要一起用。

四、步骤 1:先用日志看清谁在抓

先别急着改配置。先确认两件事:谁在抓、抓了哪些路径。

假设 nginx 用默认 combined 格式,日志长这样:

代码语言:txt
复制
203.0.113.10 - - [21/Oct/2026:10:00:00 +0800] "GET /blog/post HTTP/1.1" 200 15320 "-" "GPTBot"

统计各 AI 爬虫的请求量:

代码语言:bash
复制
for ua in GPTBot OAI-SearchBot ChatGPT-User ClaudeBot PerplexityBot CCBot Bytespider; do
  n=$(grep -c "$ua" access.log)
  printf '%-16s %8s\n' "$ua" "$n"
done

看 GPTBot 都在抓什么:

代码语言:bash
复制
# 请求行里的路径,按次数排序
grep GPTBot access.log | awk -F'"' '{print $2}' | awk '{print $2}' | sort | uniq -c | sort -rn | head -20

# 状态码分布
grep GPTBot access.log | grep -oE '" [0-9]{3} ' | sort | uniq -c | sort -rn

按天看趋势(日志被切割或压缩过就用 zcat):

代码语言:bash
复制
zcat access.log.*.gz | grep GPTBot | awk '{print $4}' | cut -c2-12 | sort | uniq -c

如果发现某个 UA 集中在 /wp-login.php、/?s= 这类路径,那多半不是真的 AI 爬虫,是扫描器在借 UA。这种直接进下一步的服务端拦截。

五、步骤 2:写 robots.txt

先给一个可以直接改的版本:

代码语言:txt
复制
# 默认放行,搜索引擎正常抓
User-agent: *
Allow: /
Disallow: /wp-admin/
Disallow: /cart/
Disallow: /checkout/
Disallow: /*?s=

# 训练类抓取:整站不抓
User-agent: GPTBot
Disallow: /

User-agent: ClaudeBot
Disallow: /

User-agent: CCBot
Disallow: /

# 检索类:保留曝光,只挡后台和交易路径
User-agent: OAI-SearchBot
Allow: /
Disallow: /wp-admin/
Disallow: /cart/
Disallow: /checkout/

User-agent: PerplexityBot
Allow: /
Disallow: /wp-admin/
Disallow: /cart/
Disallow: /checkout/

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

几个必须注意的点:

  • 不要 Disallow: /wp-content/。CSS、JS、图片都在里面,挡掉之后爬虫拿到的页面是残的,反而更容易被判定为低质量。
  • 分组之间用空行隔开,同一组的多个 User-agent 写在连续行里,规则共享。
  • 通配符支持不一致。* 和结尾的 $ 大多数爬虫支持,路径中间的通配符支持程度参差。Disallow: /*?s= 这种写法在部分爬虫上不生效,别把它当唯一防线。
  • 规则不是按顺序匹配,是按匹配长度取最具体的规则。Allow 和 Disallow 谁更长谁生效。
  • robots.txt 必须返回 200。返回 404 或 403,多数爬虫会当作「没有限制」处理,等于全放行。所以下面这步很关键:/robots.txt 这个路径本身不能按 UA 拦截。

六、步骤 3:服务端兜底

robots.txt 只对守规矩的有效,所以要有一层硬拦截。

nginx

用 map 定义变量,再在 location 里返回 403:

代码语言:nginx
复制
map $http_user_agent $aum_ai_bot {
    default            0;
    "~*GPTBot"         1;
    "~*ClaudeBot"      1;
    "~*CCBot"          1;
    "~*Bytespider"     1;
}

server {
    listen 443 ssl;
    server_name example.com;

    # robots.txt 永远放行,不加任何 UA 判断
    location = /robots.txt {
        try_files $uri =404;
        add_header Cache-Control "public, max-age=3600";
    }

    location / {
        if ($aum_ai_bot) {
            return 403;
        }
        try_files $uri $uri/ /index.php?$args;
    }
}

注意 if 放在 location 内、只用来 return,这是官方认可的用法;不要在里面写 proxy_pass 之类的指令。如果你的站有多个 location,每个都要加,或者用 limit_req 做统一的速率兜底。

Apache

代码语言:apache
复制
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (GPTBot|ClaudeBot|CCBot|Bytespider) [NC]
RewriteCond %{REQUEST_URI} !^/robots\.txt$ [NC]
RewriteRule ^ - [F]

返回什么状态码

返回 403 是明确拒绝,部分爬虫会就此放弃。不要用「200 + 空内容」——那会让对方以为抓到了空页面,还可能被记成一次成功抓取。流量特别大时用 429 配合 Retry-After 更合适。

UA 是能伪造的

所以对高价值路径,再加一层来源校验:把请求 IP 反查到域名,再正向解析一次,确认解析回来的 IP 和原 IP 一致(forward-confirmed 反查)。正经爬虫的 IP 段是公开的,可以拿来做白名单;对不上的一律按普通流量处理。

七、步骤 4:验证真的生效

配完不做验证,等于没配。四条命令,覆盖四个面:

代码语言:bash
复制
# 1. robots.txt 本体是否正常返回 200,内容是否是你写的版本
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/robots.txt
curl -sS https://example.com/robots.txt

# 2. 带 AI 爬虫 UA 请求 robots.txt —— 应该仍然是 200,不是 403
curl -sS -o /dev/null -w '%{http_code}\n' -A 'GPTBot' https://example.com/robots.txt

# 3. 带 AI 爬虫 UA 请求一个正常页面 —— 走服务端拦截应该是 403
curl -sS -o /dev/null -w '%{http_code}\n' -A 'GPTBot' https://example.com/blog/post

# 4. 普通 UA 请求同一页面 —— 必须还是 200
curl -sS -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0' https://example.com/blog/post

如果第 3 条返回 200,说明拦截没生效:检查 map 是否写在 http 块里、location 是否真的命中了这个请求、有没有 CDN 在前面把请求吃掉了。

日志侧的验证才是真验证,配置对不代表爬虫行为变了:

代码语言:bash
复制
# 拦截上线后,被拦截的请求应该出现 403
grep -E 'GPTBot|ClaudeBot' access.log | grep -oE '" [0-9]{3} ' | sort | uniq -c

# 对比上线前后三天的请求量,看是否真的下降
grep GPTBot access.log | awk '{print $4}' | cut -c2-12 | sort | uniq -c

判断标准:

  • 该 UA 的 200 数量下降,403 数量上升 → 拦截生效。
  • 403 持续几天后归零 → 对方已经放弃。
  • 403 一直很高、请求量不降 → 对方不遵守,靠服务端扛着,这是预期内的。
  • 出现了新 UA 在抓同样的路径 → 换 UA 了,回去补规则。

八、持续观察

一次配置不可能一劳永逸,UA 会变、策略会变。想省掉每天 grep 的功夫,可以用 AumCrawl 在 WordPress 后台直接看有哪些 AI 爬虫在抓你的站、分别抓了哪些路径,并按爬虫单独放行或拦截。它是免费的,和上面这套 nginx/Apache 规则可以并存——服务端做兜底,后台做日常观察。

九、常见坑清单

  1. robots.txt 放进了子目录,或者被 CMS 路由接管返回了 HTML。
  2. /robots.txt 返回 404,等于告诉爬虫「随便抓」。
  3. 用了 CDN 或对象存储,robots.txt 被缓存或覆盖,源站的修改没生效。
  4. 只拦了 GPTBot,忘了 ChatGPT-User,用户实时访问照样抓。
  5. 用 Disallow 挡了页面,又想靠页面里的 noindex 防索引,结果爬虫读不到 noindex。
  6. 拦了 /wp-content/,页面渲染残缺。
  7. 把 Crawl-delay 当限流用,实际不生效。
  8. 上了 UA 拦截但没排除 /robots.txt,爬虫读不到规则,反而全放行。

十、小结

  • robots.txt 能做的是路径级、UA 级的「礼貌通知」,对不遵守的爬虫零约束。
  • AI 爬虫按 UA 分角色,拦之前先决定你要拦哪一类:训练、检索、还是用户实时访问。
  • 真正的门禁在服务端,map + 403 是成本最低的一层。
  • 配完必须用 curl 验四件事,再用日志看 200/403 的比例变化,否则你不知道自己改了什么。

更多免费插件和用法可以看 AumCreate 插件列表。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、robots.txt 的准确定位
  • 二、能约束什么:AI 爬虫是按 UA 分角色的
  • 三、管不住什么
  • 四、步骤 1:先用日志看清谁在抓
  • 五、步骤 2:写 robots.txt
  • 六、步骤 3:服务端兜底
    • nginx
    • Apache
    • 返回什么状态码
    • UA 是能伪造的
  • 七、步骤 4:验证真的生效
  • 八、持续观察
  • 九、常见坑清单
  • 十、小结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档