Go: http.Server には必ず ReadHeaderTimeout を設定する
http.ListenAndServe や素の &http.Server{} はタイムアウトがすべて0(=無制限)。ヘッダーを少しずつ送り続けるクライアント(Slowloris)に接続と goroutine を握られ続ける。
書き方
go
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second, // ヘッダーを読み終えるまで
ReadTimeout: 30 * time.Second, // ボディまで含めて読み終えるまで
WriteTimeout: 30 * time.Second, // レスポンスを書き終えるまで
IdleTimeout: 120 * time.Second, // keep-alive で次のリクエストを待つ時間
}
log.Fatal(srv.ListenAndServe())ReadHeaderTimeoutが0だとReadTimeoutが使われ、両方0ならヘッダー読み込みに期限がない。IdleTimeoutが0だとReadTimeoutが使われる。
どの値を付けるか
- 最低限
ReadHeaderTimeoutだけは付ける。 ハンドラーの処理時間に関係しないので、どんなサーバーでも副作用なく入れられる。 ReadTimeout/WriteTimeoutは接続全体に効くので、大きなアップロード、SSE、長いストリーミングがあると途中で切れる。そういうハンドラーがあるなら、全体には付けずにhttp.NewResponseController(w).SetReadDeadline(...)/SetWriteDeadline(...)(Go 1.20+)でハンドラーごとに設定する。- ハンドラーの処理時間そのものを制限したいなら
http.TimeoutHandlerかcontext.WithTimeout。
見つけ方
gosec の G112 が ReadHeaderTimeout の付け忘れを指摘する(golangci-lint 経由でも使える)。