How to confirm an AI crawler is causing your server load, and which bot to rate-limit first A developer published a method for confirming whether AI crawlers are actually responsible for server load before applying rate limits, using standard Linux tools (nproc, uptime, vmstat) to check run-queue length and I/O wait, then adding nginx's $request_time field to access logs and running a dependency-free Python script that aggregates per-minute, per-crawler request counts, request-time seconds and 5xx errors. The guidance argues the bot to rate-limit first is the one consuming the most request-time seconds per minute within the spike window among bots worth keeping, not the one with the most requests. When the load average climbs and someone in the channel says it is the AI crawlers , that is a hypothesis, not a diagnosis. Before you add a single rate limit, three things have to be true: Short answer for step 3: the bot with the highest request-time seconds per minute inside the spike window, among the bots you have decided to keep. Not the bot with the most requests. Everything below is how to get those numbers out of a log you already have. nproc uptime vmstat 1 5 Read r run-queue length and wa I/O wait . If r is consistently above the number of cores from nproc , requests are queueing. If wa is high while us and sy are low, you are waiting on disk or the database and a web-tier rate limit will not fix it — but write the number down anyway, because you will want it again in step 6. A load average of 40 on a 4-core box and a load average of 40 on a 64-core box are different incidents. Always report the ratio. $request time into the access log The combined log format tells you that a request happened, not what it cost. Add the timing field: log format main ext '$remote addr - $remote user $time local ' '"$request" $status $body bytes sent ' '"$http referer" "$http user agent" $request time'; access log /var/log/nginx/access.log main ext; Reload, and confirm a line ends with a number: tail -n 3 /var/log/nginx/access.log If you cannot reload right now, skip the timing column — the script below treats it as optional and you can still rank by request count. Save this as crawl report.py . Standard library only, no dependencies. bash /usr/bin/env python3 """Per-minute, per-crawler request totals from an nginx access log. Usage: python3 crawl report.py /var/log/nginx/access.log crawlers.csv Expects the combined log format; the trailing $request time field is optional. """ import csv import re import sys from collections import defaultdict MONTHS = {m: f"{i:02d}" for i, m in enumerate "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split , start=1 } LINE = re.compile r'^ ?P