TP6.0自身不提供IP级限流能力,真正扛住CC攻击的是Nginx的limit_req和limit_conn;应用层黑名单仅作补漏,因TP6.0拦截已消耗PHP资源,而Nginx在请求进入应用层前毫秒级拒绝。
直接说结论:TP6.0 自身不提供 IP 级限流能力,真正扛住 CC 攻击的必须是 Nginx 层的 limit_req 和 limit_conn;应用层黑名单只能补漏,不能当主力。
ThinkPHP6.0 的中间件、行为或路由拦截都在 PHP 进程里执行,请求已经进来了、CGI 已启动、内存已分配、数据库连接可能已建立。这时候再拦,CPU 和连接资源早被耗光——CC 攻击打的就是这个时间差。
Nginx 的 limit_req 在 TCP 握手完成、HTTP 头解析后、但尚未转发给 PHP-FPM 前就做判断,毫秒级响应,不进应用层,不触发任何 PHP 逻辑。
limit_req 拒绝一个请求 ≈ 0.02ms(纯内存查表+计数)很多配置看似生效,压测一跑就崩,问题常出在 zone 定义位置、key 选择和 burst/nodelay 组合上。
limit_req_zone 必须写在 http 块顶层,不能放在 server 或 location 里,否则 reload 会报错“invalid number of arguments”$binary_remote_addr 而不是 $remote_addr:前者二进制压缩,1M 内存可存约 16000 个 IP;后者字符串存储,同样内存只存约 4000 个,容易爆 zoneburst=5 nodelay 是生产推荐组合:允许 5 个突发请求立即通过(防误杀),超了直接返回 503 Service Temporarily Unavailable;若只写 burst=5 不加 nodelay,Nginx 会排队延迟处理,攻击者反而能靠长连接耗尽 worker它解决不了流量洪峰,但能干几件 Nginx 做不了的事:识别登录态用户、关联业务行为(如 1 小时内下单 100 次)、结合 Redis 实时封禁、记录到审计日志。前提是——请求得先活过 Nginx 这关。
$_SERVER['REMOTE_ADDR'],查 Redis 黑名单(SETNX + EXPIRE)403 Forbidden 并记录 access.log,方便 Fail2Ban 后续抓日志自动封 IP$_SERVER['REMOTE_ADDR'] 是 CDN 节点 IP,得从 HTTP_X_FORWARDED_FOR 提取真实 IP,但该 header 可伪造,务必配合 Nginx 的 set_real_ip_from 配置校验很多人设了 zone=mylimit:1m,结果线上跑两天就发现限流失效——不是配置没生效,是内存满了,旧 IP 条目被强制清掉,新攻击 IP 又挤进来了。
zone=mylimit:50m
nginx -T | grep limit_req_zone 确认配置是否加载成功,再用 curl -I http://your-site/ 看响应头是否有 X-RateLimit-Limit(需额外加 echo 模块)真正卡住 CC 攻击的,从来不是某一行 PHP 代码,而是 Nginx 配置里那几行不起眼的 limit_req_zone 和 limit_req;TP6.0 黑名单不是盾,是刀——得等敌人冲过第一道门,才轮到它出手。