反爬规则上线第一周,挂掉的往往不是爬虫,而是自家监控、支付回调和 CDN 预热。本文用 10 个问答讲清 反爬白名单 该放哪些机器、怎么防伪造、怎么定期回收。
Q:为什么反爬规则一上线,先出事的是自己人?
- 五个高频事故,按发生频率排:监控拨测被判 CC、支付回调被 UA 黑名单拦、CDN 预热返回 403、SEO 工具抓不到页面、合作方接口 403。
- 根因很简单:反爬规则的判断依据是”像不像人”,而机器流量天生不像人——没有 Referer、UA 不是浏览器、请求间隔均匀、路径固定。
- 所以白名单不是人情放行名单,它本质是一份机器资产台账:谁在合法地自动化访问你,凭什么证明是它。
- 没有这份台账,规则越严误伤越多,而误伤永远靠投诉发现——投诉天生滞后,订单静默失败的时候没人报错。
Q:哪些机器必须进白名单?
- 四类必备:自建监控与拨测(含异地探针、Uptime 服务)、第三方回调(支付、短信回执、物流、IM webhook)、CDN 预热与刷新、合作方接口(开放平台、比价、ERP 对接)。
- 三类加分项:自有漏扫与基线检查、备份同步任务、站长平台的抓取诊断工具。
- 判断标准只有一句:它被拦了,会不会有人立刻来找你。会,就进名单;不会,就别进。
- 反面清单同样重要:竞价排名工具、第三方采集器、免费”网站体检”服务——请求量大又行为像爬虫,不该给白名单。
Q:按 IP 放、按 UA 放,还是按密钥放?
- 可信度排序:签名(HMAC)> 固定出口 IP > 自定义请求头 > User-Agent。UA 最容易伪造,单独用等于没做。
- IP 也会漂移:云函数出口、K8s 集群 NAT、合作方换机房,都会让固定 IP 失效。
- 工程折中:IP 段 + 自定义头双条件,两个同时成立才放行;安全要求高的回调再叠应用层验签。
# 1) 可信出口 IP(内网段 + 已登记的固定出口)
geo $trusted_machine {
default 0;
10.20.0.0/16 1; # 内网监控与任务机
198.51.100.11/32 1; # 合作方专线出口
}
# 2) 内部令牌:与 IP 段同时成立才生效,单靠请求头不放行
map "$trusted_machine:$http_x_internal_token" $wl_pass {
default 0;
"1:9f2c7a41d8e35b60" 1;
}
# 3) 规则级联:白名单优先级最高,覆盖所有拦截判定
map $wl_pass $final_block {
1 0; # 命中白名单,强制放行
0 $block; # 其余走 UA / 频率 / 路径规则
}
Q:Nginx 里怎么落地,有哪些坑?
- 顺序:白名单必须在所有
deny、limit_req、UA 判断之前生效。写在 location 里,会被上一层的if先拦掉。 limit_req按 key 限速,白名单 IP 一样会被限。正确做法是给白名单一个宽松的桶,或者把它的 key 置空——key 为空时 nginx 不做限速。allow/deny是就近匹配,多份include会互相覆盖,务必保持单一来源。- 白名单请求也要打标记,否则永远不知道名单里谁在跑量。
# 白名单单独一个宽松桶:key 为空则不限速
map $wl_pass $limit_key {
1 ""; # 空 key = 不做限速
0 $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_per_ip:20m rate=30r/s;
# 日志里带上白名单标记,便于后期审计
log_format wl '$time_iso8601\t$remote_addr\t$request_uri\t'
'$status\twl=$wl_pass';
# 统计白名单流量占比(超过 5% 就该查名单了)
awk -F'\t' '{n++; if ($5=="wl=1") w++} END {printf "wl_ratio=%.2f%%\n", w*100/n}' access.log
Q:支付回调和 webhook 怎么既防伪造又不误拦?
- 回调方 IP 往往不固定(多机房、多云),单靠 IP 白名单必然反复失效。正确分层:边缘只放路径,应用层才验真伪。
- 边缘:给回调路径单独一个 location,不套 UA 黑名单和频率规则,但保留 body 上限与超时,防止它被当成放大器。
- 应用层:HMAC 验签 + 时间窗(5 分钟)+ 事件 ID 幂等去重。三件事缺一个都可能被重放。
# 回调路径单独放行:不做 UA 判断,保留体积与超时约束
location = /pay/notify {
client_max_body_size 64k;
proxy_read_timeout 10s;
proxy_pass http://backend;
# 不写 limit_req,也不套 UA 黑名单
}
Q:CDN 预热和刷新为什么会被拦?
- 预热请求的 UA 是厂商自己的,特征是”非浏览器 UA + 单路径 + 无 Referer”,正好命中反爬规则的三条特征。
- 正确做法:向 CDN 厂商要预热节点的出口 IP 段,加进边缘白名单。只加 UA 不行,UA 谁都能写。
- 刷新走 API 接口,一般不受影响;受影响的是预热和边缘回源校验。
- 验证很直接:预热后
curl -I看x-cache。MISS 且 403,就是被自家规则拦了。
# 预热专用放行:IP 段来自 CDN 厂商公告,定期核对
geo $preheat_node {
default 0;
203.0.113.0/24 1;
}
# 验证预热是否真的进了缓存
curl -sI https://www.example.com/static/app.js | grep -i "x-cache\|age"
Q:白名单会不会变成后门?
- 会,而且这是最容易被忽视的风险:一次应急加白忘了回收,三个月后它还开着。
- 三条硬约束:名单文件独立(不混在业务配置里)、变更留痕(谁加的、为什么、到期日)、自动过期(文件名带日期,定时清理)。
- 定期审计:90 天零命中的条目进待删清单,删之前先注释七天观察。
# 白名单文件命名为 partner-2026-12-31.conf,过期自动报出来
today=$(date +%F)
for f in /etc/nginx/whitelist/*.conf; do
exp=$(basename "$f" .conf | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}')
if [ -n "$exp" ] && [[ "$exp" < "$today" ]]; then echo "EXPIRED: $f"; fi
done | tee -a /var/log/whitelist-expired.log
# 每周统计零命中的白名单条目(c=1 的基本可以清)
zcat /var/log/nginx/access-*.gz | awk -F'\t' '$5=="wl=1" {c[$2]++} END {for (i in c) print c[i], i}' | sort -n | head -20
Q:怎么验证白名单真的生效?
- 四个拨测缺一不可,少一个就有盲区:白名单 IP + 正常请求预期 200;非白名单 IP + 同请求预期 403 或 429,证明规则本身没失效。
- 白名单 IP + 恶意 UA 预期 200,这是特性不是 bug,但要知道边界在哪。
- 带签名但非白名单 IP,按设计意图判断,多数回调场景应该放行。
- 用
curl --resolve指定 IP 直连,绕过 DNS 和 CDN,避免测的是边缘而不是源站。
curl -s -o /dev/null -w "%{http_code}\n" --resolve www.example.com:443:198.51.100.11 \
-H "X-Internal-Token: 9f2c7a41d8e35b60" https://www.example.com/health
Q:白名单流量占比多少算正常?
- 一般站点 2% 以内;超过 5%,要么业务真有大量机器流量,要么名单被滥用。
- 要按类型拆开看:监控类应平稳,回调类随订单量波动,预热类集中在发布时段,合作方类取决于对接深度。
- 一个反直觉信号:某个合作方的调用量突然翻几倍,多半不是业务涨了,而是对方把你的接口当数据源在批量抓。
Q:还有什么补充建议?
- 把白名单变更和防火墙放行放在同一等级管理:走审批、留记录、有到期。低门槛加白,等于给自己留后门。
- 白名单不等于不限速。给一个宽松的桶,别给”无桶”,否则一台被入侵的白名单机器能直接把源站打满。
- 出口 IP 变更要有通知机制。在对接文档里写清”IP 变更需提前 7 天书面通知”,比事后排查省十倍力气。
- 反常识的一条:反爬做得越狠,白名单治理越重要。真正让业务出事的往往不是漏拦了爬虫,而是把自家支付回调拦成 403,订单静默失败还没人告警。