爬虫对网站的影响,往往不是"一次被爬走多少数据"这么简单。高频请求会挤占服务器资源,让正常用户访问变慢甚至超时;内容被批量抓取后,可能被搬运到其他平台,稀释你的原创价值;更糟的是,一些扫描器会顺手探测漏洞,给安全埋下隐患。在应用层写反爬逻辑当然可以,但请求已经打到后端才被拦截,成本和风险都已经产生。Nginx 作为流量的第一道入口,是设置反爬最经济、最直接的位置。
本文从实战角度出发,介绍几种在 Nginx 层面就能落地的反爬手段:请求频率限制、User-Agent 黑名单、连接数限制和请求方法过滤,并讨论它们的适用边界与组合策略。
Nginx 处理请求的效率远高于后端应用。把明显的恶意流量在 Nginx 层挡掉,有几个直接好处:
需要说明的是,Nginx 反爬属于"粗粒度过滤",它擅长挡住低成本的通用爬虫和扫描器,但挡不住精心伪装的定向爬取。所以它的定位是第一道防线,而不是全部。
限流是 Nginx 反爬中最可靠的手段。原因很简单:不管爬虫怎么伪装 IP 和 User-Agent,它高频抓取的行为本身就会暴露。 而正常用户的浏览节奏,天然远低于爬虫。
Nginx 的 limit_req 模块基于漏桶算法实现限流。先在 http 块中定义限流区域:
# 分配 10MB 内存,m.huoyisz.com约可跟踪 16 万个 IP;每个 IP 允许每秒 2 个请求
limit_req_zone $binary_remote_addr zone=one:10m rate=2r/s;这里用 $binary_remote_addr 而不是 $remote_addr,是因为二进制格式的 IP 占用内存更少,同样的内存能跟踪更多客户端。
然后在 server 或 location 中应用:
location / {
limit_req zone=one burst=5 nodelay;
# 其他配置...
}几个参数的含义:
rate=2r/s:平均速率上限。普通浏览通常每秒不到 1 次请求,2r/s 留有一定余量;如果站点资源较重,可以调低到 1r/s。burst=5:允许的突发请求数。用户打开一个页面可能同时加载多个资源,设置 burst 可以避免误伤。nodelay:突发请求立即处理而不是排队延迟。不加这个参数,超出的请求会被延迟处理,可能让正常用户感觉卡顿。对于 API 接口或下载类路径,可以设置更严格的规则,例如:
location /api/ {
limit_req zone=one burst=3 nodelay;
}被限流拒绝的请求默认返回 503。如果希望返回更明确的错误码,可以配置:
limit_req_status 429;429 Too Many Requests 语义上更准确,也方便前端做提示。
很多通用爬虫、采集工具和扫描器会在 User-Agent 里直接暴露身份,比如 python-requests、Scrapy、Go-http-client、SemrushBot 等。拦截它们成本极低。
推荐用 map 指令集中管理黑名单,而不是在 location 里堆 if。map 在请求处理早期求值,性能更好,也便于维护:
map $http_user_agent $bad_bot {
default 0;
~*SemrushBot 1;
~*AhrefsBot 1;
~*python-requests 1;
~*Scrapy 1;
~*Go-http-client 1;
~*curl 1;
}~* 表示不区分大小写的正则匹配。然后在 server 或 location 中拦截:
if ($bad_bot) {
return 403;
}这里有两个细节值得注意。
第一,返回码的选择。 403 会明确告诉对方"你被拒绝了";如果不想给对方任何反馈,可以用 Nginx 特有的 return 444,它会直接关闭连接、不返回任何内容,更省资源,也让对方难以判断拦截原因。
第二,避免误伤。 Googlebot、Bingbot 等搜索引擎爬虫带来的收录和流量通常是需要的,不要把它们加入黑名单。如果站点依赖搜索引擎收录,黑名单要谨慎维护,宁可漏掉一些可疑 UA,也不要误封正规爬虫。
另外,部分爬虫会伪造 User-Agent 冒充浏览器。这时 UA 黑名单就失效了,需要靠限流和行为分析来补位。
如果发现爬虫会同时开大量连接,可以加一层连接数限制。先在 http 块中定义:
limit_conn_zone $binary_remote_addr zone=addr:10m;然后在 server 或 location 中限制:
limit_conn addr 10; # 每个 IP 最多 10 个并发连接这个规则对正常用户几乎无感——浏览器对同一域名的并发连接数本身就有上限,10 个已经足够宽松;但对开几百个连接狂抓的爬虫,效果立竿见影。
如果网站只用于浏览,可以只放行 GET 和 HEAD,拦截其他方法:
if ($request_method !~ ^(GET|HEAD)$) {
return 444;
}这能挡掉一些用非标准方法试探的自动化工具。但要注意,如果你的站点有表单提交、登录等需要 POST 的功能,就不能一刀切,应该按路径分别配置。
实际部署时,建议把上述手段组合起来,形成分层防御:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=2r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
map $http_user_agent $bad_bot {
default 0;
~*SemrushBot 1;
~*python-requests 1;
~*Scrapy 1;
}
server {
listen 80;
server_name example.com;
location / {
limit_req zone=one burst=5 nodelay;
limit_conn addr 10;
if ($bad_bot) {
return 444;
}
}
}
}几点实践建议:
nginx -t 检查语法,再 nginx -s reload 平滑重载,避免配置错误导致服务中断。access.log 中被拦截的请求来源和 UA,据此调整黑名单和阈值。Nginx 的 limit_req 和 limit_conn 都有对应的日志变量,可以记录触发限流的请求,便于排查。location ~* \.(jpg|png|css|js)$ 单独设置更宽松的限流,避免误伤正常页面加载。必须清醒地认识到:Nginx 层面的反爬有明确的天花板。 有经验的爬虫会轮换 IP 池、随机生成 User-Agent、模拟正常浏览间隔,仅靠 IP 和 UA 很难完全防住。限流也只能提高爬取成本,无法彻底阻止分布式低速爬取。
如果内容价值高、被爬严重,应该在 Nginx 这道防线之后,叠加应用层手段:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。