单靠 Laravel 的 throttle 中间件在高并发刷量场景下大概率失效,必须分层设防——Nginx 做第一道漏桶限流 + Laravel 做精细化策略限流 + 业务层加签名/时间戳/验证码兜底。

直接说结论:单靠 Laravel 的 throttle 中间件在高并发刷量场景下大概率失效,必须分层设防——Nginx 做第一道漏桶限流 + Laravel 做精细化策略限流 + 业务层加签名/时间戳/验证码兜底。
为什么只配 throttle:60,1 没用?
常见错误是把限流当成“开关”,以为写上中间件就万事大吉。实际问题出在三处:
-
CACHE_DRIVER=array时,多 PHP-FPM 进程各自计数,限流形同虚设;生产必须切到redis或memcached - 默认按
by=request:ip,但走 CDN 或 NAT 后所有用户共用一个公网 IP,导致误杀 - 攻击者绕过登录态直接刷匿名接口,
by=auth:id根本不生效
Nginx 层必须先扛住流量洪峰
别等请求进 PHP 才处理。Laravel 处理一次请求平均耗时 200–500ms,而 Nginx 的 limit_req 是毫秒级响应。配置示例:
http {
limit_req_zone $binary_remote_addr zone=api:1m rate=5r/s;
limit_req_zone $http_x_device_id zone=device:1m rate=30r/m;
server {
location ^~ /api/ {
limit_req zone=api burst=10 nodelay;
limit_req zone=device burst=5;
}
}
}
注意点:
-
$binary_remote_addr比$remote_addr更省内存,1M 可存约 16k 个 IP -
burst=10 nodelay表示允许突发 10 个请求立即通过,不排队;没加nodelay会返回 503 - 若前端能传
X-Device-ID,用它做二级限流比 IP 精准得多
在 Laravel 里重写限流 key 才算真正可控
不能依赖默认的 by=auth:id 或 by=request:ip。必须在 AppServiceProvider::boot() 里注册自定义策略:
RateLimiter::for('api-by-tenant', function (Request $request) {
$tenant = $request->header('X-Tenant-ID') ?? 'anonymous';
return Limit::perMinute(100)->by(str_slug($tenant));
});
关键细节:
-
str_slug()是必须的——by()返回值不能含空格、斜杠、中文,否则缓存 key 冲突或报错 - 如果接口带 JWT,在
by()里解析 token 获取user_id或scope,比单纯查auth:id更快(避免 DB 查询) - 高频管理类接口(如 /api/v1/export)建议单独配策略,比如
Limit::perHour(5),防批量导出刷库
签名+时间戳才是防重放的底线
限流只能控频次,挡不住“合法包无限重发”。必须在业务层加校验:
- 前端按字母序拼接所有参数 + 时间戳 + 随机串 + 密钥,生成
X-Signature请求头 - 后端用同样逻辑复算,不一致直接
abort(401) - 时间戳偏差超过 60 秒,拒绝;随机串存 Redis(
SETNX+ TTL=60s),已存在则视为重放 - 对注册、领券、支付回调等敏感操作,强制加
captcha中间件,哪怕只是滑块验证
最易被忽略的一点:所有这些措施都依赖时钟同步。服务器时间误差 > 60 秒,时间戳校验就全崩了。上线前务必确认 ntpd 或 chrony 正常运行。


















