# Traefik static configuration: the access log this script needs. # Merge into your existing traefik.yml (or docker-compose command flags). # # The critical part is keeping the User-Agent header. Traefik drops all # headers by default, and without User-Agent every request is anonymous # and no bot analysis is possible at all. accessLog: filePath: /var/log/traefik/access.log # Traefik's default. The script parses this, not JSON. format: common bufferingSize: 100 fields: # Keep the standard CLF fields. defaultMode: keep headers: # Drop every header except the two named below: an access log # holding Cookie or Authorization is a liability, not telemetry. defaultMode: drop names: User-Agent: keep Referer: keep # Resulting line (one line, wrapped here for readability): # # 1.2.3.4 - - [10/Aug/2026:22:30:24 +0000] "GET /explore HTTP/2.0" # 200 12062 "https://git.example.com/" "Mozilla/5.0 ... ClaudeBot/1.0" # 93971 "forgejo@docker" "http://172.18.0.3:3000" 2ms # # Fields, in order: # 1 client IP <- verified against vendor ranges # 2 ident, 3 user (always "-") # 4 timestamp <- day bucketing # 5 request line <- path, for the per-path breakdown # 6 status, 7 bytes # 8 Referer (kept above) # 9 User-Agent <- bot token # 10 request counter # 11 router name <- CRAWLER_ROUTER filter, e.g. forgejo@docker # 12 backend URL, 13 duration # # Mount the log directory so both Traefik and the host can see it: # # volumes: # - /opt/traefik/logs:/var/log/traefik # # Rotate it. Traefik reopens on USR1; without rotation this file grows # without bound (it reached 533 MB in nine months on the instance this # was written for). /etc/logrotate.d/traefik: # # /opt/traefik/logs/*.log { # daily # rotate 14 # compress # delaycompress # missingok # notifempty # postrotate # docker kill --signal=USR1 traefik 2>/dev/null || true # endscript # } # # The script reads rotated and gzipped files too, as long as # CRAWLER_LOG keeps its trailing glob (access.log*). # --- If you sit behind Cloudflare ------------------------------------- # The logged client IP will be Cloudflare's edge, not the real visitor, # and no such request can be IP-verified (the report counts these in a # separate "via CF" column). To recover real IPs, trust CF's ranges on # the entrypoint: # # entryPoints: # websecure: # address: ":443" # forwardedHeaders: # trustedIPs: # # https://www.cloudflare.com/ips-v4 - keep this list current # - 173.245.48.0/20 # - 103.21.244.0/22 # # ... remaining CF ranges ... # # Only list address ranges you actually trust: anyone in trustedIPs can # spoof X-Forwarded-For and forge their apparent source address.