
最近帮几个朋友的Magento 2站点做性能优化,踩了不少爬虫相关的坑。站点流量看着还行,但服务器动不动就卡死,CDN账单莫名暴涨,一查日志才发现是被各种 Bots 爬到崩溃。一个不守规矩的爬虫,能在几个小时内把服务器负载翻倍,把你的带宽费用打出天际。
这篇文章把Bot流量识别、合法爬虫优化、恶意爬虫拦截讲清楚,全是可落地的方案,Nginx、Varnish、应用层都有覆盖。
Magento 是出了名的重型框架。每个未缓存的页面请求都要经历:
真实用户一次访问也就看个5-10个页面,而一个爬虫一小时能扫5万个URL。这个差距不用我多说了吧。
假设你有个5万SKU的商城,一个不懂事的爬虫把所有商品链接、筛选组合、分页链接全走一遍,产生的请求量能把你真实用户的访问量秒成渣。
Bot 流量典型的杀伤效果:
catalog_product_entity 表如果你的统计分析里出现一堆"直接访问"、跳出率100%、页面停留时间0秒,很可能就是 Bot 流量在伪装成真实用户。
动手之前先搞清楚状况。从访问日志入手是最直接的方式。
# 按 User-Agent 统计请求量 TOP 20
awk -F '"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # 按 IP 地址统计请求量 TOP 20
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20 # 统计 Bot 流量总量
awk '/Googlebot|bingbot|AhrefsBot|SemrushBot|MJ12bot/ {sum++} END {print sum}' /var/log/nginx/access.log
在 Magento 内部扩展 Http 上下文或者用 Observer 记录可疑模式:
<?php
namespace Vendor\BotLogger\Observer; use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\HTTP\PhpEnvironment\Request;
use Psr\Log\LoggerInterface; class LogBotTraffic implements ObserverInterface
{ public function __construct( private Request $request, private LoggerInterface $logger ) {} public function execute(Observer $observer): void { $userAgent = $this->request->getHeader('User-Agent') ?? ''; $ip = $this->request->getClientIp(); // Detect known bots $bots = ['Googlebot', 'bingbot', 'AhrefsBot', 'SemrushBot', 'MJ12bot', 'DotBot']; foreach ($bots as $bot) { if (stripos($userAgent, $bot) !== false) { $this->logger->info('BOT_TRAFFIC', [ 'bot' => $bot, 'ip' => $ip, 'url' => $this->request->getRequestUri(), 'time' => microtime(true) ]); break; } } }
}
把这个日志单独存,配合 Grafana、阿里云日志服务或者腾讯云监控来追踪。
一个配置得当的 robots.txt 是引导合法爬虫远离高消耗页面的最简单手段。
文件放在 pub/robots.txt(如果你 webroot 是 pub/ 的话):
User-agent: *
Disallow: /admin/
Disallow: /checkout/
Disallow: /customer/
Disallow: /catalogsearch/
Disallow: /sales/guest/
Disallow: /wishlist/
Disallow: /compare/
Disallow: /review/product/post/
Disallow: /newsletter/
Disallow: /sendfriend/
Disallow: /catalog/product_compare/
Disallow: /*?*price=
Disallow: /*?*color=
Disallow: /*?*size=
Disallow: /*?*material=
Disallow: /*?*limit=
Disallow: /*?*order=
Disallow: /*?*dir=
Disallow: /*?*mode= # Allow CSS/JS/images for rendering
Allow: /pub/media/catalog/product/
Allow: /pub/static/ # Sitemap for efficient crawling
Sitemap: https://yourstore.com/sitemap.xml # Crawl delay for all bots
Crawl-delay: 2
如果你的 webroot 是 pub/(安全最佳实践),robots.txt 要放在 pub/robots.txt。别放错了位置。
robots.txt 只能防君子,不能防小人——恶意爬虫根本不会理它。
在 Web 服务器层面做限流,能保护 PHP-FPM 和 MySQL 不会被冲垮。
# In http context (nginx.conf)
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $bot_zone zone=bots:10m rate=1r/s; # Map user agents to bot classification
map $http_user_agent $bot_zone { default ""; ~*(Googlebot|bingbot) $binary_remote_addr; ~*(AhrefsBot|SemrushBot) $binary_remote_addr; ~*(MJ12bot|DotBot|BLEXBot) $binary_remote_addr; ~*(Screaming|Majestic) $binary_remote_addr; ~*crawler $binary_remote_addr; ~*spider $binary_remote_addr;
} # In server context (site config)
server { # General limit for all traffic limit_req zone=general burst=20 nodelay; # Stricter limit for identified bots location / { if ($bot_zone != "") { limit_req zone=bots burst=5 nodelay; } try_files $uri $uri/ /index.php$is_args$args; } # Always allow static assets (they're cached and cheap) location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ { limit_req off; expires 1y; add_header Cache-Control "public, immutable"; }
}
进阶玩法是用 OpenResty/Nginx 配合 Lua,根据行为动态封禁 Bots:
-- nginx.conf lua_shared_dict blocklist 10m;
init_worker_by_lua_block { local blocklist = ngx.shared.blocklist -- Preload known bad actors blocklist:set("185.220.101.0/24", true, 86400)
} access_by_lua_block { local blocklist = ngx.shared.blocklist local ip = ngx.var.remote_addr if blocklist:get(ip) then ngx.exit(ngx.HTTP_FORBIDDEN) end -- Block by excessive requests (detected via log analysis) local req_count = blocklist:incr(ip .. ":count", 1, 0, 60) if req_count > 100 then blocklist:set(ip, true, 3600) -- Block for 1 hour ngx.log(ngx.WARN, "Auto-blocked IP for excessive requests: ", ip) ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS) end
}
Varnish 可以区分人类和 Bots,给他们用不同的缓存策略。
sub vcl_recv { # Normalize user agent for bot detection if (req.http.User-Agent ~ "(Googlebot|bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot)") { set req.http.X-Bot = "true"; } else { set req.http.X-Bot = "false"; } # Don't cache admin, checkout, customer area for anyone if (req.url ~ "^/(admin|checkout|customer)/") { return (pass); } # Cache product pages for bots with longer TTL (they don't need fresh prices instantly) if (req.http.X-Bot == "true" && req.url ~ "^/catalog/product/view/") { unset req.http.Cookie; return (hash); } # For humans, respect cookies if (req.http.Cookie) { # Strip all but essential cookies set req.http.Cookie = regsuball(req.http.Cookie, "(^|;)\s*__[a-z]+=[^;]*", ""); set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); if (req.http.Cookie == "") { unset req.http.Cookie; } }
} sub vcl_backend_response { # Cache product pages for bots longer if (bereq.http.X-Bot == "true" && bereq.url ~ "^/catalog/product/view/") { set beresp.ttl = 1h; set beresp.grace = 6h; } # Cache category pages for everyone if (bereq.url ~ "^/catalog/category/view/") { set beresp.ttl = 15m; set beresp.grace = 1h; }
} sub vcl_deliver { # Add debug header (remove in production) set resp.http.X-Cache-Status = req.http.X-Bot;
}
不是所有 Bots 都守规矩。有些根本不管 robots.txt,伪装 User-Agent,疯狂爬你的站。
可以用 Nginx Bad Bot Blocker 或者自己写个精简版:
# Block known bad user agents
if ($http_user_agent ~* (ahrefsbot|semrushbot|mj12bot|dotbot|blexbot|ezooms|yandexbot|baiduspider|sogou|exabot|facebot|facebookexternalhit)) { # Allow search engines, block SEO tools and scrapers if ($http_user_agent !~* (googlebot|bingbot|slurp|duckduckbot)) { return 444; #