Structured stderr logging is always on; --log-format selects plain, json, or cat. --no-access-logs suppresses only the access record below — operational records (upstream reconnects, verify-on-hit warnings, startup) still emit. Storage URLs and rule secrets are redacted from all records.

The access record

One record per request or tunnel, message access, level info.

Field Always Meaning
client yes Source identity used for rule selection (recovered client IP behind a trusted proxy)
acceptor yes Which listener/transport accepted the connection
method yes HTTP method, or CONNECT for a tunnel; empty when the request never parsed
url yes Absolute request URL, or the CONNECT authority
outcome yes Same vocabulary as requests.total's outcome label — see below
elapsed yes Total handling time
dst when resolved Pinned upstream literal ip:port; absent before/without resolution (e.g. an early no-match)
principal when authenticated The auth.http_proxy identity, for attribution only — it does not select rules
status when an HTTP status exists Absent for raw splices and pre-response failures
error on failure Opaque failure detail; do not parse it for classification — use outcome/status

Because outcome is identical to the metric label, a log-based alert and a metric alert classify a request the same way. Key outcome values:

  • ok — forwarded (or spliced) successfully.
  • no-match — no connection-phase rule matched; client got 511.
  • block — a rule blocked it (no upstream contacted).
  • forwarding-denied — egress policy or the self-listener guard denied the resolved destination.
  • auth-required / auth-failed — proxy-auth gate outcomes.
  • misdirected-authority, invalid-request, invalid-request-shape, headers-too-large, unsupported-absolute-scheme — request-shape rejections.
  • splice (mitm:false) — a raw tunnel was opened; no status.

Building alerts and dashboards

Alert on outcome transitions the metrics can't dimension per-client: e.g. a single client producing sustained auth-failed, or forwarding-denied for a dst you expected to allow. Group by acceptor to separate explicit-proxy from transparent traffic. The high-cardinality fields (client, url, principal, dst) live only here and in traces — never on metrics.

Source: internal/proxy (http1.go, http2.go, auth.go).