ip黑名单和请求限流必须分开实现,且都不能挂错位置、不能信$_server['remote_addr']、不能用非原子redis操作——否则要么拦不住,要么误杀静态资源,要么高并发下直接失效;需通过nginx set_real_ip_from+real_ip_header配置穿透代理,php层优先取x-real-ip并过滤私有地址,黑名单用apcu或redis毫秒级查询,限流用tinywan/limit-traffic保障lua原子性,中间件须全局注册、排除静态资源、正确放行。

直接结论:IP黑名单和请求限流必须分开实现,且都不能挂错位置、不能信$_SERVER['REMOTE_ADDR']、不能用非原子Redis操作——否则要么拦不住,要么误杀静态资源,要么高并发下直接失效。
怎么拿到真实客户端IP才不翻车
在Nginx + PHP-FPM或CDN后,$_SERVER['REMOTE_ADDR']基本等于127.0.0.1或代理出口IP,完全不可信。必须穿透代理链取真实IP:
- Nginx配置里必须加
set_real_ip_from 10.0.0.0/8;(填你可控的代理网段),并设real_ip_header X-Forwarded-For; - PHP层优先读
$request->header('x-real-ip'), fallback时解析$request->header('x-forwarded-for'),取最左非私有地址(过滤127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、::1) - IPv6地址别用
ip2long()——它返回false;改用inet_pton()+inet_ntop()
IP黑名单中间件怎么写才不拖垮服务
黑名单查得慢,中间件本身就成了攻击入口。重点不是“怎么写”,是“怎么查得快”:
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 小名单(几百条以内):存APCu,用
apcu_fetch('ip_blacklist')查,热更新靠filemtime()比对时间戳,不每请求stat - 大名单(万级+):用Redis
SET存IP,中间件里调$redis->sismember('blacklist:ips', $ip),毫秒级响应 - 绝对不要在中间件里写
Db::table('blacklist')->where('ip', $ip)->count()或file_get_contents('blacklist.txt')——QPS上千时磁盘或DB直接拖垮 - 中间件必须注册在
config/middleware.php的''键下(全局中间件),否则拦不住/favicon.ico等请求 - 务必排除静态资源:
if (str_starts_with($request->getUri()->getPath(), '/static/')) return $handler($request);
限流中间件为什么总超限?关键在Redis原子性
用webman/rate-limiter但没开Lua,或者用INCR + EXPIRE手动拼,高并发下必然超限——两个请求同时读到99,各自+1变成101,这就是“超卖”。
- 首选
tinywan/limit-traffic:默认用Lua脚本封装原子操作,key格式为rate_limit:{ip}:{route}:{timestamp},自带X-Rate-Limit-Limit等标准响应头 - 若坚持用
webman/rate-limiter,确认Redis驱动版本≥6.0.12,且use_lua配置项为true(1.1.0+默认开启,旧版需手动开) - key设计必须带客户端标识:
$request->ip()在反代后不可用,要换成$request->header('x-real-ip') - 配置
[100, 60]意思是“60秒内最多100次”,不是60分钟——单位错就全乱套
中间件挂载位置和签名错一个就静默失效
Webman中间件不是写了类就能用,挂错位置或签名不对,请求照进控制器,防护形同虚设:
- 必须实现
Webman\MiddlewareInterface,process()方法签名固定为process($request, $handler),返回Webman\Http\Response实例 - 别漏
return $handler($request)放行;别用die或exit终止——要返回response()->withStatus(403)或response()->json(..., 429) - 全局中间件会拦
/favicon.ico和/static/,不加白名单过滤,JS/CSS加载失败是常态 - 路由组中间件只在Webman ≥1.0.12支持,旧版本必须每个子路由单独挂
->middleware([]),否则不生效
最易被忽略的点:IP黑名单和限流中间件都依赖真实IP,但Nginx配置漏了set_real_ip_from或PHP层没做私有地址过滤,就会把整个内网段当成一个IP封掉,或者把CDN出口IP当真实用户反复计数。这问题上线后才暴露,排查成本远高于写代码本身。










