AIによるスパイクアクセスを受けたので、 robots.txtの調整や AWS WAFによる対策を試みてみた

AIによるスパイクアクセスを受けたので、 robots.txtの調整や AWS WAFによる対策を試みてみた

Anthropic社ClaudeBotから1時間5万件、毎分1500件のスパイクアクセスが発生しました。 これを受けて実施した robots.txt設定とAWS WAF設定の見直し、AIの完全排除を回避した対策について紹介します。
Clock Icon2025.02.24

AWS WAFのBotControlルールにおいて、AIカテゴリに分類されるスパイクアクセスが発生。
動的生成される記事ページへのリクエストが、1時間あたり5万件、ピーク時には1分間に1500件記録されていました。

当サイトで公開中の5万件強の全記事に匹敵するリクエストが発生した原因の調査と、実施した対策について紹介します

CloudWatchメトリクス確認

事象の原因を特定するため、AWS WAFのメトリクスを分析しました。

bot:category AI の 急増

AIカテゴリのリクエスト数が、1時間あたり5万件まで顕著に増加しました。

他のカテゴリ(search_engine: Google、Bing など、social_media: X、Facebook など)には大きな変動は見られませんでした。

WAFラベルAI3ヶ月

  • LabelNamespace="awswaf:managed:aws:bot-control:bot:category"
  • LabelName="ai",search_engine, social_media

bot:name claudebot

リクエスト急増時において、「AI」カテゴリと「ClaudeBot」というbot:nameが一致しました。
このことから、Anthropic社からのリクエストが急増したと判断しました。

WAF_ai_claudebot

  • LabelNamespace="awswaf:managed:aws:bot-control:bot:name"
  • LabelName="claudebot"

アクセスログ解析

より詳細な分析と対策の検討を行うため、CloudWatch Logsに保存されたCloudFrontのアクセスログ(標準アクセスログv2)を、Logs Insightsを用いて解析しました。

IP、UserAgent別集計

リクエスト急増時間帯のアクセスログから、ClaudeBot の UserAgent を持つ IP アドレスを求めました。

fields @timestamp, @message
| parse @message /\"time-taken\":\"(?<timetaken>[^\"]+)\"/ 
| parse @message /\"c-ip\":\"(?<clientip>[^\"]+)\"/ 
| parse @message /\"cs\(User-Agent\)\":\"(?<useragent>[^\"]+)\"/ 
| parse @message /\"x-edge-response-result-type\":\"(?<edge_response_result_type>[^\"]+)\"/ 
| filter tolower(useragent)  like /claudebot/
| stats count() as request_count, 
        sum(timetaken) as total_time, 
        avg(timetaken) as avg_time 
  by clientip,useragent
| sort request_count desc
| limit 10000

IPとUA毎の集計

  • 記録されたIP数は1888
  • 1つのIPアドレスから100〜200リクエスト程度発生していました。

同時アクセスIP数

ClaudeBotのログを対象に、1 秒間当たりのユニークな IP アドレス数を確認しました。

fields @timestamp, @message
| parse @message /\"time-taken\":\"(?<timetaken>[^\"]+)\"/ 
| parse @message /\"c-ip\":\"(?<clientip>[^\"]+)\"/ 
| parse @message /\"cs\(User-Agent\)\":\"(?<useragent>[^\"]+)\"/ 
| filter tolower(useragent) like /claudebot/
| stats count(*) as total_requests,
        count_distinct(clientip) as unique_ips
  by bin(1s)
| sort by unique_ips desc

ユニークIP数

  • 最大で32個のユニークなIPアドレスが同時にアクセスしており、並列数30前後でリクエストが行われていた可能性が示唆されました。

URL別集計

リクエストされたURI別の確認を試みました。

fields @timestamp, @message
| parse @message /\"time-taken\":\"(?<timetaken>[^\"]+)\"/ 
| parse @message /\"cs\(User-Agent\)\":\"(?<useragent>[^\"]+)\"/ 
| parse @message /\"cs-uri-stem\":\"(?<uri>[^\"]+)\"/ 
| parse @message /\"sc-status\":\"(?<status>[^\"]+)\"/ 
| filter tolower(useragent) like /claudebot/
| stats count() as request_count, 
        sum(timetaken) as total_time, 
        avg(timetaken) as avg_time 
  by uri,useragent, status
| sort total_time desc

URL別集計

  • 正規記事ページのリクエストが 2.5 万件確認されました。

x_host_header別集計

同一記事へのアクセスが複数回確認されたため、リクエスト先ホストを確認しました。

fields @timestamp, @message
| parse @message /\"time-taken\":\"(?<timetaken>[^\"]+)\"/ 
| parse @message /\"cs\(User-Agent\)\":\"(?<useragent>[^\"]+)\"/ 
| parse @message /\"x-host-header\":\"(?<x_host_header>[^\"]+)\"/ 
| parse @message /\"sc-status\":\"(?<status>[^\"]+)\"/ 
| filter tolower(useragent) like /claudebot/
| stats count() as request_count, 
        sum(timetaken) as total_time, 
        avg(timetaken) as avg_time 
  by x_host_header,useragent, status
| sort total_time desc

x_host_header別集計

公開 URL のホスト名とは異なるホスト名でのリクエストが確認できました。

個別リクエスト

UserAgentに 「claudebot」を含むIPアドレスを求め、そのリクエスト内容を確認しました。

fields @timestamp, @message
| parse @message /\"time-taken\":\"(?<timetaken>[^\"]+)\"/ 
| parse @message /\"c-ip\":\"(?<clientip>[^\"]+)\"/ 
| parse @message /\"cs\(User-Agent\)\":\"(?<useragent>[^\"]+)\"/ 
| filter tolower(useragent)  like /claudebot/
| filter clientip  like /172.71.##.##/
| limit 100

個別リクエストログ

  • 1つのIPアドレスから1分間のリクエスト数は10件程度

対策

robots.txt

anthropic社のガイドに倣い、robots.txtに、1秒のクロール遅延(Crawl-delay)を要求する設定を追加。
過剰なクロールの抑制する記述を試みました。

$ curl -s https://dev.classmethod.jp/robots.txt | grep 'Crawl-delay:'
Crawl-delay: 1

クローリング活動を制限するために、robots.txtの非標準のCrawl-delay拡張をサポートしています。例えば以下のようになります:

User-agent: ClaudeBot
Crawl-delay: 1

Anthropicはウェブからデータをクロールしているのか、そしてサイト所有者はどのようにクローラーをブロックできるのか?

代替ドメイン

意図せぬクロール対象になったFQDNは、サービス開始前は関係者向けの動作確認用。サービス開始後は性能監視のための外形監視に利用していたものでした。

検索サイトなどに当該FQDNを含むURLが登録される事をさけるため、「x-robots-tag: noindex」を戻す設定としていましたが、クロールを抑止できませんでした。

< HTTP/2 200 
< date: Fri, 21 Feb 2025 **:**:** GMT
< content-type: text/plain
(...)
< x-robots-tag: noindex

今回の事象発生を受けて正規サイトへリダイレクトする設定に変更。

< HTTP/2 301 
< date: Fri, 21 Feb 2025 **:**:** GMT
< content-type: text/html
(...)
< location: https://dev.classmethod.jp/

リダイレクト数が収束した後、当該FQDNは廃止予定です。

Block

AWS WAF のレートルール、カスタムキーとしてラベルを利用して、指定期間内に一定以上のリクエストが発生した場合に遮断を実現する事は可能です。

インデックスの登録、更新漏れなどの副作用を避けるため、遮断は極力避ける方針ですが、robots.txt などの効果がなく、ClaudeBotのスパイクアクセスが継続的に発生する場合には、以下のルールをAWS WAFの ALCsに追加する事で、ClaudeBot と判定されたリクエストの制限を開始する予定です。

  • bot-control 判定ラベルを利用したレート制限 (1分間に100以上でBlock発動)
{
    "Name": "rate-limit-claudebot",
    "Priority": 250,
    "Statement": {
        "AndStatement": {
            "Statements": [
                {
                    "LabelMatchStatement": {
                        "Scope": "LABEL",
                        "Key": "awswaf:managed:aws:bot-control:bot:name:claudebot"
                    }
                },
                {
                    "RateBasedStatement": {
                        "Limit": 100,
                        "AggregateKeyType": "CUSTOM_KEYS",
                        "CustomKeys": [
                            {
                                "Type": "LABEL_NAMESPACE",
                                "Key": "awswaf:managed:aws:bot-control:bot:name"
                            }
                        ],
                        "TimeWindow": 60
                    }
                }
            ]
        }
    },
    "Action": {
        "Block": {}
    },
    "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "RateLimitClaudeBotRule"
    }
}

まとめ

AIエージェントの排除は、AIを活用した検索対象から外れたり、更新情報の反映漏れなど、読者・執筆者双方に不利益を招くリスクがあると考え、AWS WAFによる遮断措置は極力避ける方針です。

今回、robots.txt によるクロール頻度の宣言を試みましたが、追加で 「llms.txt」の整備を行い、LLM(大規模言語モデル)向けの指示を明記する事で、正規コンテンツにクローラーを誘導する事で、意図せぬリクエストの抑制を図ります。

また、インフラ側についても改善すべきボトルネック要素が発覚した事をうけ、バックエンドシステムの最適化や、オリジンの直接アクセスを排除する施策として、CloudFrontのVPCサポートや、AWS WAFの活用なども図っていきたいと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.