This library was born out of a desire to have a basic understanding of my web projects’ visitors and a lack of desire to set up anything.
clj-simple-stats, as the name suggests, is trivial to set up:
- Add
io.github.tonsky/clj-simple-stats {:mvn/version "1.1.0"}todeps.edn. - Add
clj-simple-stats.core/wrap-statsanywhere in your middleware stack.
That’s it! There’s no step 3.
- All the visits that go through that middleware will be counted automatically.
- Data will be stored to
clj_simple_stats.duckdbin the current dir. - You’ll get a dashboard at
/statsthat looks like this:
Couple of highlights:
- Statistic is fully server-side, no JS is used.
- You can narrow down stats per URL, query, referrer, user agent, etc. (click on the looking glass icon).
- We try our best to distinguish between human visits, RSS readers, and scrapers.
- The goal is to understand how many people see your site, not how many requests.
- Only responses with status 200 and content-type:
text/html,application/atom+xmlorapplication/rss+xmlare counted - Tracking cookie is used only to uniquely identify visitors; no cookie banner is needed.
- Only two external dependencies: DuckDB and Ring (which you probably have anyway).
These default values are used; feel free to override:
(wrap-stats handler
{:db-path "clj_simple_stats.duckdb"
:uri "/stats"
:dash-perms-fn (fn [req] true)
:cookie-name "stats_id"
:cookie-opts {:max-age 2147483647
:path "/"
:http-only true}})wrap-stats is a composition of wrap-collect-stats and wrap-render-stats, which you can use separately as well.
Finally, wrap-render-stats middleware checks if (= (:uri opts) (:uri req)) and then calls render-stats handler. If you prefer to use your own router, feel free to use render-stats handler directly.
Q: Will this work with static websites? E.g. served fully by Nginx? A: Unfortunately, no. This is designed to sit in your Ring middleware stack.
Q: How can I select time intervals? A: Select whole years at the top of the page. Select whole months by clicking their labels in the graph. Everything else only by modifying URL params. Might change later.
You can see live deployments at
Copyright © 2025 Nikita Prokopov
Licensed under MIT.
