Records one page view per HTML response — the whole tracker, server-side.
Add it once to the host's browser pipeline:
# lib/my_app_web/router.ex
pipeline :browser do
# … existing plugs …
plug PhoenixKitWebAnalytics.Plug
endThat's the entire installation. No script tag, no client-side bundle, no cookie, and nothing added to the rendered page — pages stay byte-for-byte what they were.
Cost to a request
The plug does three cheap things in the request process: a method/path check,
one ETS read for settings, and register_before_send/2. When the response is
on its way out it builds a map and hands it to
PhoenixKitWebAnalytics.Collector, which does the settings-dependent
enrichment, the session-stitch query, and the insert in a supervised task.
No database work happens while the client is waiting.
What is skipped
Anything that isn't a person looking at a page:
- non-
GETrequests, and non-2xx responses (a redirect isn't a page view) - responses that aren't
text/html, so assets, JSON APIs, and file downloads never appear in reports - paths matching the exclusion patterns in settings (
/admin*by default) - requests sending
DNT: 1orSec-GPC: 1, whenweb_analytics_respect_dntis on (it is by default) - automated User-Agents, unless
web_analytics_track_botsis on - anything explicitly marked with
skip/1
Options
:exclude— extra path patterns on top of the ones in settings, e.g.plug PhoenixKitWebAnalytics.Plug, exclude: ["/healthz", "/internal*"]. A trailing*makes a pattern a prefix match.
Client IP
conn.remote_ip is used as visitor-hash input (and only that — no IP is ever
stored). Behind a proxy or load balancer that is the proxy's address, which
would collapse every visitor into one. The right fix is a plug that rewrites
remote_ip from the forwarding headers your infrastructure actually
controls — remote_ip — placed before
this one in the pipeline. Failing that:
config :phoenix_kit_web_analytics, trust_x_forwarded_for: truereads the first entry of X-Forwarded-For. That header is client-settable, so
only turn it on when something upstream is guaranteed to overwrite it.
Summary
Functions
Marks a request as not-to-be-tracked.
Whether this request has been marked to skip tracking.
Functions
@spec skip(Plug.Conn.t()) :: Plug.Conn.t()
Marks a request as not-to-be-tracked.
Useful for endpoints that render HTML but aren't pages — a preview iframe, a health check that returns a status page, a LiveView upload target:
conn |> PhoenixKitWebAnalytics.Plug.skip() |> render("preview.html")Also honoured when set before this plug runs, e.g. from an earlier plug.
@spec skipped?(Plug.Conn.t()) :: boolean()
Whether this request has been marked to skip tracking.