はじめに
私の運営しているunityroom.comですが、2025-07-11からの数日間、DDoS攻撃喰らってエラーまみれでした

赤いのがエラー
| Request Timeouts: There have been 867763 timeout errors
86万件のタイムアウトとか笑うしかない(笑えない)
また攻撃される可能性はありますけども一旦は解決済みです
期間中にサイト利用いただいていた皆さまにはご不便をおかけしてすみませんでした
攻撃されていた間、HerokuのH99という見慣れないエラーが多発してたんですが、その原因について勉強になったのでブログに書いておきます
H99エラーの発見と放置
7月11日、オートスケーリングの通知がいつもより大量にきていることに気がつきました

普段は1〜3dynoで稼働していたものが、4台とか5台とかまでスケールアウトしていました(Judoscaleの契約プランの制限でMAX5台)

この時見慣れないエラー(H99)を見つけました
Heroku のエラーコード | Heroku Dev Center
説明を読むと、
これは、Heroku プラットフォームでの内部エラーを示します。 修正するにはユーザーのアクションが必要なその他のすべてのエラーとは異なり、 このエラーはアクションが不要です。 すぐに再試行するか、ステータスサイトを確認してください。
とのこと
ほーん、Heroku内部でなんか起きてるんやな。最近デプロイもしてないしアプリケーション側の問題ではなさそうだな。としばらく様子見することに
サポート問い合わせと回答
翌日になっても一向に収まらないのでH99はどうしたらいいのかサポートに問い合わせ
土日はサポート返信ないので平日にやっと繋がり、調べるから待っててねとのこと
そして7/15にやっと調査結果が届きました
以下が和訳したものの抜粋
デバッグ中に、エラーの周期的なスパイクが発生していることに気付きました(グラフの時間はUTCです)
H99エラーは、アプリのdynoが応答しなかったため、ルーターによって隔離されたことが原因で発生していました。
このアプリは、H12エラーの処理で手一杯になっており、使用可能なNodeプロセスをすべて使い切ってしまっていたようです。その結果、ルーターは新しいリクエストをそのdynoに送信できず、dynoを隔離しました。そしてこのパターンが繰り返されているように見えます。スパイクが発生した時間帯と、何らかの重い処理が実行されていたかどうかを確認していただけますか?
H99エラーと同じ時間帯に発生していたH12エラーも、アプリケーションのリソース不足を示しています。弊社のルーターは、30秒以上かかっているリクエストを打ち切りますが、バックエンドのdynoはその処理を継続しようとします。ルーターはそれを認識していないため、そのdynoに対して新たなリクエストを送信し続けてしまい、結果としてdynoが飽和していきます。そのため、静的アセットなど無関係なURLに対してもH12エラーが発生することがあります。H13エラーも原因としては似ていますが、主に並列Webサーバーの問題に関連しています。
このような事象を防ぐには、timeoutのようなものを導入し、長時間かかるリクエストをdynoレベルでも打ち切るようにするとよいでしょう。これにより「Response timeout」例外が発生し、リクエストが中断されます。
この対策を入れることで、問題の連鎖的な悪化を防ぐことができますが、それでも長時間実行される処理は根本的に対処する必要があります。New Relicなどのモニタリングツールを使用して、アプリ内の長時間実行される処理を可視化し、それを最適化してください。すべてのリクエストは500ミリ秒以内に完了することを推奨します。
もし処理自体が本質的に時間のかかるものであれば、それらはバックグラウンドワーカーにオフロードすることを検討してください。
めちゃくちゃ詳しく調べてくれてる!ありがてぇ
サポート返信やリンクされている以下のページを見ると、 dynoがリクエストを受け付けられなくなると H99エラーがでるとのこと!
まじか〜。H99はプラットフォームエラーでこちらでは何もできないと思っていたけど、がっつりこちら側が原因じゃん
対応
NewRelicは前から入れてあったので、該当のエラー時間帯のトランザクションを調査

なぜかusers#showが一番時間かかっている(普段はそんなことない)
さらに掘り下げると、なぜか同一ユーザーIDのページに大量にリクエストが来てるっぽい

Solarwinds(旧Papertrail)のリクエストログを探してみると、84万件ヒット

NewRelic側のアプリケーションログを検索してみると、67万件ヒット

SolarwindsのログからIPを調べたけれど一貫性はなし
こりゃDDoSだなぁ〜ってことでCloudflareへ
IP指定ではブロックできなさそうなのでパス指定でブロックルールを作成

結果
ブロックルールを作成してから待つこと1時間

来たー!(大量のブロックされたリクエスト)

詳細をみてみるとたった2〜3分で世界中から5万リクエストぐらい届いてました
そりゃ捌ききれんよ
Heroku側はいつも通りの平穏を取り戻しました。
反省
最初からNewRelicとか眺めてもう少し深掘りしていればDDoSの兆候には気づけたなぁ〜〜〜〜と反省
解決に時間がかかってしまい申し訳ありませんでした。
