Visitor analytics

lazysite records its own traffic (SM140), so visitor statistics work out of the box - no web-server log access, no cookies, no JavaScript beacon. The processor appends one compact JSON line per request to lazysite/logs/access-YYYYMMDD.jsonl, and the Visitor Stats plugin (enabled on Plugin Manager) aggregates it for the manager Stats page.

The first-party access log

Every request the processor serves is recorded: page views (cache hits included), clean-URL 404s and scanner probes. Each line carries the time, path, status, truncated user-agent, external referrer, bytes, channel (operator traffic is marked and excluded from visitor statistics) and the request Host for per-domain splitting.

The log is anonymised at write: the visitor field is a daily-salted keyed hash of the address - the same visitor yields the same key within a day, so unique visitors are "distinct daily visitors" - and the IP itself is never stored. Attacker-controlled fields are stripped of control characters and length-capped against log injection. Daily files are pruned after retention_days (default 90); writes are single O_APPEND appends that never block or break serving.

Traffic classes

Because there are no cookies and no JS, every classification is a log-only heuristic over user-agent, path and status - an honest estimate, not authenticated identity. Requests are classed as:

  • human - the headline audience; totals, top pages, trend and referrers count only this class
  • ai - AI assistants and model fetchers (GPTBot, ClaudeBot, PerplexityBot, ...), plus non-browser clients on the automation endpoints
  • bot - crawlers, monitors and headless/automation tooling
  • noise - probe paths (wp-login.php, .env, any .php), secret-fishing, infrastructure fetches (favicon, robots, sitemap, feeds) and 404ed build-manifest probes
  • logged_in - operator activity on the manager surface

Referrer spam is dropped from the external-referrers report, and self-referrers count as on-site navigation. Your own automation is kept out of human by convention: give it a user-agent containing lazysite-agent/<partner-id>.

Configuration

Settings live in lazysite/stats.conf, edited on the plugin's config page:

Key Default Meaning
first_party true Record first-party traffic; off = read the web-server log instead
retention_days 90 Days of first-party log files kept
window_days 30 Reporting window for the Stats page
top_n 15 Rows in the top-pages / top-referrers tables
ai_user_agents - Extra UA substrings to count as AI
noise_paths - Extra path prefixes to treat as noise

The web-server access log survives as the fallback source when no first-party data exists (and its error log as a summarised diagnostics tier); log paths are auto-detected or set by the server owner at install time - never by a site manager.

The analyse_visitors action

analyse_visitors (control API and MCP, gated by the analytics capability) returns a sanitised analytics export for AI reasoning: totals, per-class shares, a per-day series, top pages, referrers, status codes and a capped event stream - never a raw log line, a filesystem path or a visitor IP. Ingestion is incremental and cached, so each call parses only new log lines; pass window (days, up to 365) to widen the view.

Notes

  • Browsing analytics complement the audit trail, which records material actions (auth, edits, deletes) - not browsing
  • On a site with no auth store, the visitor key is salted from a dedicated random salt, so keys stay non-reversible
  • Successful static-asset serves handled directly by the web server are only visible to the fallback/diagnostics tier