- 作者帖子
背景
电商生产站 (WooCommerce) 两次 CPU 爆表,load 从正常的 ~3% 飙到 77%(第一次) 和 68%(第二次),php-fpm 全部 worker 被打满,站点返回 HTTP 000(连接超时) 。
根因
大量爬虫发送 GET 请求,URL 携带
add-to-cart=XXXXX¤cy=XXX参数。这类请求会触发 WooCommerce 完整的加购逻辑 (DB 查询 + session 写入),远比普通页面浏览重。关键特征:
- 请求方法: GET(正常加购应该是用户点击按钮触发 POST)
- 无 WooCommerce session cookie(说明不是真实用户)
- 来自大量不同 IP,伪装各种 Chrome UA
- 密集翻页:
/items/page/N?add-to-cart=XXXXX
sar 数据和 access log 交叉验证,499 状态码 (客户端超时断开) 占比超过 60%,证实 php-fpm 被打满后新请求全部排队超时。
止血过程 (三层→收敛为一层)
第一层:mu-plugin(PHP 层,已清理)
最初在
wp-content/mu-plugins/部署 PHP 拦截脚本,检查 GET + add-to-cart + 无 session cookie → 返回 403 。问题:mu-plugin 在 WordPress 完整加载后才执行,每个请求仍然消耗 php-fpm worker 2-3 秒。爬虫量大时 worker 照样被占满。
第二层:auto_prepend_file(PHP 引擎层,已清理)
通过
.user.ini注册auto_prepend_file,在 WordPress 加载之前就执行拦截逻辑。优势:毫秒级返回 403,worker 几乎不被占用。
局限:仍然经过 php-fpm,高并发时仍有开销。第三层:nginx 规则 (最终方案,保留)
在 nginx vhost 配置中,用 nginx 变量组合实现条件判断:
# Anti-bot: block GET add-to-cart without WooCommerce session if ($request_method = GET) { set $block_atc ""; } if ($arg_add-to-cart) { set $block_atc "${block_atc}Y"; } if ($http_cookie !~* "wp_woocommerce_session_") { set $block_atc "${block_atc}N"; } if ($block_atc = "YN") { return 403; }逻辑:GET 请求 + 有 add-to-cart 参数 + 无 WooCommerce session cookie → nginx 直接返回 403,PHP 完全不参与。
nginx 不支持
if嵌套,所以用字符串拼接模拟 AND 条件——这是 nginx 的经典技巧。$block_atc只有在三个条件都满足时才等于"YN"。收敛
确认 nginx 层生效后,清理了 mu-plugin 和 auto_prepend_file,只保留 nginx 单点拦截。减少维护负担,避免多层规则相互干扰。
效果
- Load:41 → 1.2(5 分钟内恢复)
- 爬虫请求:nginx 直接 403,零 PHP 消耗
- 正常用户:有 WooCommerce session cookie,不受影响
- 正常加购:真实用户浏览器带 session cookie,正常通过
经验总结
- WooCommerce GET add-to-cart 是天然攻击面:任何人拼个 URL 就能触发完整加购逻辑,且 WooCommerce 默认不做频率限制
- PHP 层拦截治标不治本:即使返回 403,php-fpm worker 仍被占用。高并发场景必须在 nginx/CDN 层拦截
- nginx 多条件判断用字符串拼接:
set $var "${var}X"+ 最终if ($var = "XY")是标准做法 - auto_prepend_file 是好的中间方案:当没有 nginx 权限时,比 mu-plugin 快几个数量级
- 最终应该上 CDN 防护(如 Cloudflare):在边缘节点就挡掉,连 nginx 都不用处理
后续跟进:两个新问题
问题一:Cavalcade 僵死任务导致反复 CPU 爆表
nginx 爬虫规则部署后,CPU 仍然反复飙高。排查发现第二个根因:Cavalcade(WordPress 后台任务调度器) 积压了大量僵死任务。
现象:94 万条 failed 任务堆积,4 个 running 状态的任务从 2 月 26 日就开始了,419 个 waiting 任务全部过期被不断重新调度,WP Rocket SaaS 任务单个吃 78% CPU 。
根因:Cavalcade runner 的 Worker 类完全没有超时机制。 PHP CLI 模式 max_execution_time=0,一个任务挂起后 worker 永远不会被回收。
修复:Worker 加 300 秒超时 (SIGTERM→SIGKILL),max_workers 4→2,清理 94 万 failed jobs + 770 万 logs,重启 3 个 systemd service 。
问题二:nginx cookie 检测规则误杀正常客户
原规则逻辑:没有 woocommerce_session cookie 的 GET add-to-cart 请求返回 403 。问题是 WooCommerce 的 session cookie 在第一次加购之后才设置,新客户第一次点加入购物车就被 403 了。
修复:改用 rate limit 。 nginx.conf http 块加 map+limit_req_zone,按 add-to-cart 参数选择性限速 (rate=6r/m burst=3 nodelay),超限返回 429 。正常用户一分钟内不会连续加购 4 次以上,爬虫批量请求被 429 且零 PHP 消耗。
补充经验
- Cavalcade 没有 worker 超时,部署必须自己加
- Cookie 检测要考虑首次交互时 cookie 还不存在的情况
- nginx map+limit_req 是 WooCommerce 最佳按参数限速方案
- 作者帖子
Cyberforums · 原文 · Markdown
想回复或继续讨论?请到 Cyberforums 赛博论坛参与。
参与讨论 / 回复