/settings/weird-stats — fleet-level patterns that don’t compose into a single picture on any of the regular per-site or per-server dashboards. Read-only, no interaction, one page with seven blocks: four summary tiles, four “selling point” stats, and ranked tables.
Why it exists
Everything else in Clockwork Control is scoped to a site, a server, or a single issue. Weird Stats is deliberately the opposite — it exists to answer questions that only make sense fleet-wide: which paths get hit hardest across every site at once, whether Cloudflare-proxying measurably reduces attack volume, which IPs show up in more than one server’s jail. Nothing here drives an alert or a workflow; it’s for spotting a pattern a human wouldn’t otherwise go looking for.
The seven blocks
WeirdStatsAggregator has public methods wired through by WeirdStatsController::index() with no view-layer logic of its own:
| Method | Block | Window |
|---|---|---|
summaryTiles() |
Total sites, attacks (7d), active bans, unprotected-site count | 7d / live |
settlingPointStats() |
Four sub-stats below (CF reduction, auto-ban speedup, tier density, self-ban prevention) | mixed |
pluginCoverageMatrix() |
Gatekeeper/LLAR/Wordfence coverage broken down by Cloudflare state (LLAR is a detected-state count only — Control no longer installs it) | live |
topSitesByVisits() |
Top 10 sites by visits, with concentration framing (top-10 % of fleet, 1st:10th ratio) | 30d |
worstRepeatOffenders() |
Top 10 banned IPs ranked by distinct servers hit, not raw ban count — the “this attacker is everywhere” framing | all-time |
unprotectedSitesByTraffic() |
Sites with no Gatekeeper, LLAR, or Wordfence, sorted by 30-day visits so the highest-traffic exposure sorts to the top | 30d |
settlingPointStats() in turn covers four sub-stats meant to quantify the value of specific operator setup choices rather than surface a problem:
cf_attack_reduction— attacks-per-site for Cloudflare-proxied sites vs. sites not using Cloudflare, expressed as a percent reduction. Returnsnullwhen either bucket has zero sites (nothing to compare).auto_ban_speedup— average review-queue decision time forauto-repeatbans vs.manualones, expressed as a speedup factor.tier_density— servers/sites/avg-sites-per-server broken down by theDedicated/Shared/Stagingtags.self_ban_prevention— how many would-be bans the CF/fleet-IP ignore-list matcher dismissed at ingest over the last 30 days (decided_by = 'system-cf-filter'), plus coverage numbers (fleet server count, CF IP ranges covered).jail_protected_ipsis hardcoded to 0 — fail2ban jail state isn’t mirrored into this app in real time, and the last audit found 0 protected IPs actually in an active jail, so it’s reported as a floor rather than computed live.
What “visits” means here
A visit is a distinct IP per site per UTC day, excluding 403 responses and static-asset paths (.js, .css, images, fonts) — the WP Engine-style definition, closer to “humans who looked at the site today” than “page views.” Source is the hourly threat_logs → site_traffic_daily rollup (clockwork:rollup-traffic; see Features → Traffic + capacity).
Two known biases the page itself documents in a <details> disclosure, worth repeating here since they directly affect how to read the Top Sites and Unprotected Sites tables:
- Bots are not filtered out at rollup time — doing so made the rollup query 50-100x slower, so sites without Cloudflare have visit counts inflated by bot traffic.
- Cloudflare-proxied sites under-count — the origin only ever sees CF edge IPs, so distinct-IP-per-day collapses every real visitor behind the same handful of edge addresses. Cross-CF-state comparisons are noisier than the raw numbers suggest.
Caching
Stats sourced from threat_logs (the attacks_7d tile and cfAttackReduction()) are cached for 60 minutes under weird_stats:* keys. Everything else (sites, blocked_ips, site_traffic_daily — all small or already pre-rolled-up) runs live on every page load; v1 doesn’t cache those. Heavy aggregation queries that previously scanned tens of millions of raw threat_logs rows (mostAttackedPaths() and attackHourHistogram()) were retired to prevent web timeouts and page crashes.
clockwork:warm-weird-stats runs every 9 minutes (*/9 * * * *, withoutOverlapping(15), backgrounded) to keep the cache warm. The 60-minute TTL is deliberately far longer than the 9-minute warm cadence: if the warmer itself dies for an hour, a visitor still sees slightly-stale cached data instead of a cold cache miss.
Security note
SecurityCheck’s cache_credential_leak check scans weird_stats:* cache entries for anything credential-shaped, since these are the largest cached payloads in the app and a natural place to double-check nothing sensitive leaked into a cache key or value. See Architecture → Security model.
