Go应用层防DDoS需分层实施:Accept阶段关空连接、请求头读取超时、按IP复用rate.Limiter;长连接须限并发数;HTTP层设三重超时并禁Keep-Alive;前置CDN/高防IP比代码更关键。

Go 应用层防 DDoS 不是靠“拦住所有坏请求”,而是让恶意流量在抵达业务逻辑前就失败——比如在 Accept 阶段关掉空连接、在读取请求头时超时退出、用复用的 rate.Limiter 按 IP 拒绝过频访问。真正有效的策略必须分层落地,且每层都得避开常见误用。
按 IP 复用 rate.Limiter,别用全局单例
直接 new 一个 rate.Limiter 放进 handler,等于给每个请求新建桶,既泄漏内存又锁竞争;用同一个实例给所有 IP 共享令牌,又完全失去限流意义。
- 必须用
sync.RWMutex+map[string]*rate.Limiter按需创建并复用,key 是清洗后的 IP(strings.Split(r.RemoteAddr, ":")[0]) -
rate.NewLimiter(10, 20)表示每秒最多 10 个请求、最多积压 20 个令牌;登录页等敏感路径建议收紧到(2, 3) - 避免在 HTTP handler 内做
limiter.Wait()—— 它会阻塞 goroutine;改用limiter.AllowN(time.Now(), n)做非阻塞判断
长连接服务必须限制并发数,不能只靠限流
HTTP 短连接限流够用,但 Livego、WebSocket 或自研信令服务这类长连接场景,攻击者建 1000 个空 TCP 连接就能耗尽 net.Conn 和 goroutine,rate.Limiter 根本没机会触发。
- 在
net.Listener.Accept()后立刻检查活跃连接数,超限时直接conn.Close()并记录日志;不要等到 TLS 握手或协议解析完成 - 用
atomic.Int64替代sync.WaitGroup或全局计数器:高并发下Add()/Done()争抢严重,Load()/Store()更轻量 - Livego 的
max_connections: 1000只是内部统计值,不自动拒绝新连接;需在protocol/rtmp/accept.go类似位置手动加守门逻辑
HTTP 层必须设超时并禁用 Keep-Alive 滥用
Slowloris、R-U-Dead-Yet 等慢速攻击不发完整请求,只维持低速发送或半开连接,专门耗尽服务器 read buffer 或连接表。
- 启用
http.Server.ReadTimeout、WriteTimeout、IdleTimeout,三者都要设,且IdleTimeout建议 ≤ 30s - 明确关闭 Keep-Alive:设置
http.Server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))清空 HTTP/2 支持,并在响应头中加Connection: close - 不要依赖
context.WithTimeout包裹 handler —— 它只控制业务逻辑执行时间,对连接建立、TLS 握手、header 读取无效
最易被忽略的一点:应用层防御永远只是最后一道防线。哪怕你把 rate.Limiter 调得再细、atomic 计数写得再稳,若源站 IP 直接暴露在公网上,攻击者绕过 Go 服务直打内核协议栈,所有应用层逻辑都形同虚设。CDN 回源、高防 IP、SYN Cookie 开启,这些前置动作比任何 Go 代码都关键。


















