- Python 100%
| README.md | ||
forgejo-crawlers-statistics
Crawler monitoring for the Forgejo instance at git.asxp.io / git.czsk.it.
Reads the Traefik access log, mails a threshold alert daily and a summary
weekly, and separates real AI crawlers from the ones only claiming to be.
Why IP verification
A user-agent header is self-declared and free to forge. Measured over 2.27M requests (Nov 2025 - Aug 2026), most traffic labelled as an AI crawler did not come from the vendor it named:
| Bot | Claimed | Verified | Spoofed |
|---|---|---|---|
| Claude-User | 5,000 | 0 | 4,992 |
| ChatGPT-User | 2,670 | 13 | 2,657 |
| PerplexityBot | 2,038 | 0 | 846 |
| ClaudeBot | 4,301 | 1,070 | 852 |
| Google-Extended | 879 | 0 | 747 |
| GPTBot | 1,864 | 766 | 1,098 |
The forgeries share infrastructure: a pool of ~100 Google Cloud hosts
rotates through six vendor identities, sometimes within the same hour.
Their requests target /.env, /terraform.tfstate,
/serviceAccountKey.json — credential harvesting wearing a crawler
badge. Verified bots, by contrast, made zero robots.txt violations
across 2,682 requests and never fetched repository source.
So the report counts a request as legitimate only when the source IP falls inside the vendor's published range list.
Verification sources
| Vendor | Endpoint |
|---|---|
| OpenAI | openai.com/{chatgpt-user,gptbot,searchbot}.json |
| Anthropic | claude.com/crawling/bots.json |
developers.google.com/static/crawling/ipranges/*.json |
|
| Perplexity | perplexity.ai/perplexitybot.json |
| Apple | search.developer.apple.com/applebot.json |
Meta, ByteDance, Amazon, Diffbot and Cohere publish no range file; their traffic is reported but cannot be verified by IP.
Ranges are fetched at run time and cached at
/var/cache/crawler-alert/ranges.json. A failed fetch falls back to
the cache and the mail says so — a vendor outage degrades the report
rather than breaking it.
Modes
crawler-alert.py # daily: mail only if yesterday > 100k requests
crawler-alert.py --weekly # Monday: 7-day summary, always mails
crawler-alert.py --dry-run # print instead of mailing
Only --weekly fetches ranges; the daily threshold check stays fast.
Caveats
Cloudflare-proxied requests cannot be verified. For traffic arriving
through Cloudflare the logged IP is the CF edge, not the client. Those
hits are counted in a separate via CF column rather than being called
real or fake. Enabling a real-IP header in Traefik would resolve them.
Perplexity's list is stale (8 prefixes, last updated 2025-02) and Apple's dates to 2023, so some genuine traffic from those two may fail verification.
Install
sudo install -m755 crawler-alert.py /usr/local/bin/
sudo cp crawler-*.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now crawler-alert.timer crawler-weekly.timer
SMTP credentials are read from Forgejo's own app.ini [mailer]
section via docker exec, so there is no second copy to maintain.
Recipient and threshold are constants at the top of the script.
Requirements
Python 3.9+ (stdlib only), read access to /opt/traefik/logs/access.log,
and docker exec on the forgejo container. Traefik must keep the
User-Agent header in its access log:
accessLog:
filePath: /var/log/traefik/access.log
fields:
headers:
defaultMode: drop
names:
User-Agent: keep