反爬规则拦不住爬虫,最多算白干;拦住自己人,才是真事故。搜索引擎蜘蛛被误伤会掉收录,合作方接口被拦会掉服务,监控探针被拦会掉告警,而这三种损失往往要等几天才暴露。本文用 9 个问答给出一份反爬规则上线前的自查清单。
Q:反爬规则上线前,最容易出的问题是什么?
- 不是拦不住,而是拦错了却没人发现。三类误伤最常见:
- 误伤搜索引擎蜘蛛:收录和抓取量悄悄下滑,等发现时已经掉了一批关键词排名。
- 误伤合作方与自有系统:支付回调、数据接口、监控探针、拨测节点被拒,业务和告警同时出问题。
- 误伤真实用户:公司出口、客户办公网、运营商大 NAT 出口被整片封禁。
- 共同点是故障静默:日志里全是 403 和 429,但没人盯着看。所以上线前的重点,是先把「怎么发现拦错」和「怎么快速撤回」准备好。
Q:怎么先做到只记录不拦截?
- 任何新规则都应该先跑观察模式,取一个完整周的数据,覆盖爬虫抓取高峰和业务大促窗口。
- 做法有两种,按你的拦截位置选:
- 规则层直接指定观察:Nginx 的限流支持只记录不拒绝。
- 打标记而不拒绝:用变量给可疑请求打上标记,写进响应头和日志,前端与日志先照常工作。
# 1) 限流只记录不拒绝(观察期专用)
limit_req_dry_run on;
# 2) 按 UA 打标记,先不拒绝:加到日志里观察命中量
map $http_user_agent $bot_flag {
default 0;
~*(python-requests|scrapy|curl|wget|headlesschrome) 1;
}
add_header X-Bot-Flag $bot_flag always;
add_header X-Limit-Flag $limit_req_status always;
- 观察期的验收标准:规则命中量稳定、被命中对象里能明确区分出「该拦」和「不该拦」,再进入下一步。
Q:白名单要列哪些,顺序怎么排?
- 白名单不是可选项,漏一项就可能是一次线上事故。建议逐条对照:
- 搜索引擎蜘蛛的真实来源 IP 段;自有办公网、机房、跳板机与 CI 构建机;监控探针与拨测节点;压测出口;支付与短信等第三方回调;有授权协议的采集方;CDN 回源节点。
- 顺序上牢记一点:Nginx 的 allow 与 deny 按出现顺序从前到后匹配,命中即结束。所以白名单必须写在通用拦截规则前面。
# 白名单统一放在文件最上方,命中即 return
geo $whitelist {
default 0;
10.0.0.0/8 1;
203.0.113.0/24 1; # 压测出口
198.51.100.7 1; # 监控探针
}
map $whitelist $limit_key {
1 "";
0 $binary_remote_addr;
}
server {
# 先放行,再拦截
allow 10.0.0.0/8;
allow 203.0.113.0/24;
deny all;
}
- 需要注意:空 key 的请求通常不计入限流,这正是白名单的常见写法,上线前请在你们实际使用的 Nginx 版本上验证一次。
Q:怎么确认来访的搜索引擎蜘蛛是真的?
- 只看 User-Agent 一定被绕过,UA 是客户端随手就能改的字符串。正向解析证明不了身份,必须做反向解析:
# 由来访 IP 反查域名,再正向查回来比对,两次一致才算真蜘蛛
dig -x 1.2.3.4 +short
# 输出应形如 crawl-1-2-3-4.baidu.com / crawl-1-2-3-4.googlebot.com
# 正向确认:解析结果必须能回到同一个 IP
dig +short crawl-1-2-3-4.baidu.com
- 再叠加官方公布的 IP 段清单做二次校验,两者都过才放行。
- 提醒一句:蜘蛛 IP 段会被转卖和冒用,通过反解校验只能挡掉大部分,长期还要看抓取行为是否合理(抓取频率、路径分布、是否遵守 robots 协议)。
Q:多层规则打架,怎么快速排查?
- 反爬规则通常分散在 CDN、WAF、Nginx 三层,最常见的现象是只看一层配置怎么都找不到拦截来源。
- 四条排查路径:
- 看响应头指纹:不同层的拒绝返回的 Server、状态码和自定义头不一样,先判断是哪一层拒绝的。
- 看 nginx 生效后的完整配置:
nginx -T输出的是合并与 include 之后的真实配置,比逐个看文件可靠。 - 核对 location 优先级:精确匹配
=最高,其次是^~前缀,再是正则,最后是普通前缀。规则写在低优先级段里可能永远不生效。 - 检查 return 与 rewrite 的顺序:
return会在早期阶段直接结束请求,写在它后面的规则等于不存在。
# 抓真实拦截响应,看是哪一层拒绝、返回什么
curl -sI -o /dev/null -w '%{http_code} %{time_total}\n' -A 'python-requests/2.31' https://www.example.com/
# 打印合并后的完整配置,确认规则真的在了、顺序对不对
nginx -T 2>/dev/null | grep -n -A3 -E 'limit_req|limit_conn|deny|allow'
Q:灰度上线的节奏怎么安排?
- 建议三段式,每段之间留出观察窗口:
- 第一段观察:跑 7 天,只记录不拦截,产出一份命中对象清单。
- 第二段小范围:只对最明确的一类(例如 UA 完全匹配的扫描器)真正拒绝,观察 2 至 3 天。
- 第三段全量:确认无异常后放开到行为类规则。避开大促、发版和投放窗口,别在流量高峰改规则。
- 对拿不准的规则,可以只在小流量入口或非核心路径上先生效,核心链路留到第二天。
Q:上线后必须盯哪些指标?
- 四个指标能覆盖绝大多数误伤:
- 被拒比例:403 与 429 占全部请求的比例,超过日常基线就要看是谁在被拒。
- 真实蜘蛛抓取量:在站长平台看抓取频次与收录数,比在服务器上数 UA 更准。
- 业务成功率:下单、支付、登录等核心接口的成功率不能被反爬规则影响。
- 告警与探针存活:监控探针一旦被拦,你会连告警都收不到,这是最危险的静默故障。
# 每天扫一遍:被拒请求的 TOP 来源与 TOP 路径
grep -E ' 40[13] | 429 ' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
grep -E ' 40[13] | 429 ' access.log | awk '{print $7}' | sed 's/?.*//' | sort | uniq -c | sort -rn | head -20
Q:出问题了怎么一键回滚?
- 回滚速度取决于上线前有没有把规则拆开:
- 把整套反爬规则放进独立文件,主配置里只留一行 include,回滚就是注释掉这一行再 reload。
- 保留上一版配置副本,改完先用
nginx -t校验语法,再 reload。 - 记录每一条规则的负责人、上线时间和到期日,避免临时规则半年后还挂在线上。
# 主配置只保留一行
include /etc/nginx/conf.d/anti-scraping/*.conf;
# 回滚三步
sed -i 's|^include .*anti-scraping.*|# &|' /etc/nginx/nginx.conf
nginx -t && nginx -s reload
nginx -T 2>/dev/null | grep -c 'limit_req_zone' # 应为 0
Q:还有什么补充建议?
- 把这份清单固化成上线流程:观察期数据齐全、白名单逐条核对、反解校验蜘蛛、灰度分三段、指标有基线、回滚一行生效,六项缺一项就别上线。
- 规则宁可少而稳,不要一次上几十条。规则越多,冲突与误伤的组合越多,排查成本呈指数上升。
- 每季度做一次外部视角复查:从站外扫一遍自己的防护规则、核对白名单是否还准确,比事后救火便宜得多。