Turf Stats

Àpèjúwe

Turf is a self-hosted, cookieless analytics plugin. No Google Analytics, no
Jetpack, no external network calls Ìtumọ̀ Yorùbá: – everything is measured and stored in your
own WordPress database.

Why Turf

  • Cookieless. Visitor deduplication uses a one-way hash of IP + user-agent,
    never a cookie and never the raw IP address.
  • Self-hosted. All data lives in your own database; nothing is sent to any
    third party.
  • Zero configuration. Every public post type and taxonomy is tracked
    automatically, including ones you add later.

What it tracks

  • Page views (deduplicated and raw), unique visitors, views per visitor, and
    average visit duration
  • Device, browser, operating system, language, and country (via CloudflareÌtumọ̀ Yorùbá: ’s
    header when available Ìtumọ̀ Yorùbá: – no GeoIP lookups)
  • Referrers / traffic sources, UTM campaign parameters, and automatic outbound
    link tracking (full destination URL + the page clicked from)
  • New vs. returning visitors, scroll depth, and reading time
  • On-site search terms, including zero-result searches
  • Visitor routes (page-to-page transitions), landing pages, and a bounce-rate
    proxy Ìtumọ̀ Yorùbá: – all reconstructed from sessions
  • Cache offload: how much traffic is served by a page cache (SiteGround,
    Cloudflare, …) instead of hitting WordPress
  • 404 hits, comments per period, editorial content activity, per-author stats
  • Bots & LLM crawlers (Googlebot, GPTBot, ClaudeBot, …) Ìtumọ̀ Yorùbá: – tracked server-side
    and kept separate from human visitor numbers
  • WooCommerce funnel (views, add to cart, checkout) and form submissions
    (Contact Form 7, Gravity Forms) when those plugins are active

The admin pages use native WordPress postboxes: collapsible, draggable,
hideable via Screen Options, and styled by your admin color scheme. The
interface is English and ships with a full Dutch (nl_NL) translation.

Àwọn àwòrán ìbòjú

Ìgbéwọlẹ̀

  1. Install and activate the plugin.
  2. ThatÌtumọ̀ Yorùbá: ’s it Ìtumọ̀ Yorùbá: – views start counting immediately for every public post type and taxonomy. Find the reports under the “Statistics” menu in wp-admin.

Optional extras (a visible view counter, click tracking on specific elements, social-share links, importing historical Jetpack/entry-views view counts via the “Statistics > Import” admin page or WP-CLI) are documented in the pluginÌtumọ̀ Yorùbá: ’s README on GitHub.

FAQ

Does Turf set cookies or require a consent banner?

Turf sets no cookies and sends no data to any external service. Deduplication
uses a one-way hash of IP + user-agent; the raw IP is never stored. It is
designed to fit a “legitimate interest” basis for first-party, aggregate
analytics Ìtumọ̀ Yorùbá: – but that is not legal advice: check with whoever maintains your
privacy policy.

What data does Turf collect, and where does it go?

Per page view Turf stores, in your own database only: a one-way visitor hash,
timestamp, the page viewed, device type / browser / OS family (parsed from the
user-agent), language, country code (only if Cloudflare provides it), referrer
hostname, UTM parameters, scroll depth, and reading time. Raw event rows are
pruned automatically after 18 months (filterable); aggregate totals are kept.
Nothing is ever transmitted off your server.

Why are TurfÌtumọ̀ Yorùbá: ’s numbers lower than JetpackÌtumọ̀ Yorùbá: ’s or a raw hit counterÌtumọ̀ Yorùbá: ’s?

“Views” is deduplicated: a repeat view of the same page by the same visitor
within 30 minutes doesnÌtumọ̀ Yorùbá: ’t count again (the same default Google Analytics and
Matomo use). The separate “Raw views” number counts every page load, for an
apples-to-apples comparison with hit-counting tools.

Does it work with page caching?

Yes. Tracking runs client-side, so cached pages are still counted. Turf even
reports how much of your traffic is served from cache.

Where is the country data coming from?

From CloudflareÌtumọ̀ Yorùbá: ’s CF-IPCountry request header, if your site is behind
Cloudflare. Turf deliberately does not call any geolocation API Ìtumọ̀ Yorùbá: – that would
mean sending visitor IPs to a third party. Without Cloudflare, country stays
empty unless you hook up your own local database via the turf_visitor_country
filter.

Does uninstalling remove my statistics?

By default, no Ìtumọ̀ Yorùbá: – analytics history is precious, so the data survives an
uninstall/reinstall. To remove everything on uninstall, add
define( ‘TURF_REMOVE_DATA_ON_UNINSTALL’, true ); to wp-config.php (or set
the turf_remove_data_on_uninstall option to 1) before deleting the plugin.

Where do I suggest a feature or report a bug?

On the pluginÌtumọ̀ Yorùbá: ’s GitHub repository: https://github.com/fbloemhof/turf-stats/issues

Àwọn àgbéyẹ̀wò

Kò sí àwọn àgbéyẹ̀wò fún plugin yìí.

Àwọn Olùkópa & Olùgbéejáde

“Turf Stats” jẹ́ ètò ìṣàmúlò orísun ṣíṣí sílẹ̀. Àwọn ènìyàn wọ̀nyí ti ṣe ìkópa sí plugin yìí.

Àwọn Olùkópa

Túmọ̀ “Turf Stats” sí èdè rẹ.

Ṣe o nífẹ̀ẹ́ sí ìdàgbàsókè?

Ṣàwárí koodu, ṣàyẹ̀wò ibi ìpamọ́ SVN, tàbí ṣe àgbékalẹ̀ sí àkọsílẹ̀ ìdàgbàsókè nípasẹ̀ RSS.

Àkọsílẹ̀ àwọn àyípadà

1.23.0

  • Custom date ranges: pick any start/end date instead of just the fixed
    presets, and save frequently-used ranges by name to jump back to them
    later.
  • CSV export for the overview totals, top pages, top referrers,
    device/browser/OS breakdown, search terms, and 404s boxes Ìtumọ̀ Yorùbá: – respects
    whatever period (including a custom range) is on screen.
  • Weekly/monthly email report: a summary of your stats sent to one or more
    addresses on a recurring schedule, with a “Send test email now” button to
    check formatting/delivery without waiting for the schedule.
  • Fixed several period-filtered boxes (404s, per-author stats, click and
    outbound-link breakdowns, WooCommerce funnel, peak-hours heatmap) silently
    including data all the way through “now” instead of stopping at the end
    of “Yesterday” or a picked date Ìtumọ̀ Yorùbá: – only affected those two period choices,
    not the day/week/month tabs.
  • Dutch (nl_NL) translation updated for all new strings.

1.22.0

  • Track the utm_content campaign parameter (e.g. InstagramÌtumọ̀ Yorùbá: ’s
    link_in_bio) alongside the existing utm_source/utm_medium/
    utm_campaign, with a matching “Campaign content (UTM)” breakdown box.

1.21.1

  • Fixed the “Formulieren” (Forms), “Top search terms” and “Most discussed”
    boxes on the Analysis/Statistics pages silently disappearing for any
    period other than “All time” Ìtumọ̀ Yorùbá: – a broken date filter made the underlying
    query invalid SQL, so it failed silently and the box was dropped.

1.21.0

  • New [turf_stats] shortcode: an at-a-glance table (online now, today,
    yesterday, last 7 days, last 28 days) for a sidebar Text/HTML widget or
    Shortcode block, with metric (views/visitors), layout (table/list)
    and items attributes to customize which rows show.
  • Fixed a corrupted Dutch (nl_NL) .mo file (duplicate “Online now” source
    string made it fail to compile, and the compiled file itself wasnÌtumọ̀ Yorùbá: ’t a
    valid sorted .mo) Ìtumọ̀ Yorùbá: – Dutch translations were silently not loading.

1.20.3

  • Screen resolution breakdown labels are now English source strings with
    Dutch translations in the language file (was accidentally Dutch in code).
  • Daily chart window math extracted into a shared turf_day_window() helper
    so the single-day and 7-day windows no longer duplicate the timezone logic.
  • Fixed TURF_VERSION (the cache-busting version for all enqueued JS/CSS)
    being stuck at 1.19.0 since the 1.20.0 release Ìtumọ̀ Yorùbá: – every asset change since
    then was shipped under an unchanged version string.

1.20.2

  • Fixed daily bar chart on the Today/Yesterday/fixed-date tabs: it showed
    only the anchor day (plus a sliver of the day before under a non-UTC site
    timezone) instead of the intended 7-day context window. The chart query
    now spans the 7 days ending on the selected day, matching the 7 bars it
    renders. Regression from 1.20.0 (date navigation).

1.20.1

  • Screen resolution: fall back to viewport innerWidth/innerHeight in the
    tracking JS when window.screen reports 0 or is missing (in-app webviews,
    fingerprinting-protected browsers) so fewer views land in “unknown”.
  • Screen resolution breakdown now separates App/REST API views (which never
    run the tracking JS and legitimately have no screen size) from real browser
    views that failed to report one, instead of lumping both into a single
    “unknown” bucket.

1.20.0

  • Added date navigation (native date picker, Yesterday tab) with fixed-date sentinel fix.
  • Added top-referrers ‘Toon meer’ toggle (shows 10, reveals up to 50).
  • Added dashboard widget (online now, todayÌtumọ̀ Yorùbá: ’s views/visitors with % vs yesterday), reusing overview card styling.
  • Added review-nudge snooze (random 30-90 days, max 3x then permanent).
  • Added device-class label (Phone/Tablet/Desktop) at screen resolution.
  • Fixed 90-day chart overflow (gap + horizontal scroll).
  • Dutch translations for new strings.

1.19.0

  • New “Screen resolution” breakdown box: visitor screen sizes (in CSS
    pixels) are recorded with each view. The new database columns are added
    automatically on update.
  • New “Avg. load time” stat box: page load times are measured in the
    visitorÌtumọ̀ Yorùbá: ’s browser via the Navigation Timing API and averaged per period.
  • The “Online now” box gains a live “Currently viewed” list of the pages
    current visitors are on, refreshed on the same 20-second poll.
  • Hourly visitors chart reworked: a fixed 24-hour axis with a dashed
    “yesterday” line for direct comparison against the previous day.
  • Daily views/visitors bars now show their values on the chart, with
    density-based label sizing on the 30- and 90-day views.
  • The Caching box now explains itself when a cache layer is detected but
    is serving (almost) none of the tracked page views.
  • Much faster legacy view-count import: a Jetpack top-posts pre-pass
    covers the most-viewed posts in one request, admin-page batches run
    concurrently, and the WP-CLI command gains Ìtumọ̀ Yorùbá: –offset/Ìtumọ̀ Yorùbá: –limit flags.
  • Dutch (nl_NL) translation updated for all new strings.

1.18.1

  • Added wordpress.org listing screenshots (Statistics overview, Analysis,
    Bots & LLMs) and the corresponding == Screenshots == section. No
    functional or tracking changes.

1.18.0

  • The GitHub-based update checker is removed entirely (updater.php and its
    vendored library) Ìtumọ̀ Yorùbá: – releases now deploy straight to WordPress.org via a
    GitHub Action, and updates are handled by wordpress.org like any other
    hosted plugin.
  • Fixed postbox drag-order and collapse/expand state not actually being
    saved: the admin pages were missing the nonce fields WordPress coreÌtumọ̀ Yorùbá: ’s own
    postbox AJAX handlers look for, so those requests silently failed. Order
    and collapsed state now persist correctly per user.
  • The site-specific “Dorpsapp” companion-app integration is now a generic,
    opt-in connector-app mechanism (turf_connector_app_route_patterns /
    turf_connector_app_label filters) Ìtumọ̀ Yorùbá: – no behavior change unless you were
    using the old turf_dorpsapp_route_patterns filter, which is renamed.
  • Added a GitHub issues link (bug reports / feature suggestions) to the FAQ.
  • The legacy view-count import (Jetpack Stats / the old entry-views plugin) is
    now also available as a “Statistics > Import” admin page, alongside the
    existing wp turf-stats import-legacy-views WP-CLI command Ìtumọ̀ Yorùbá: – same import,
    run in small AJAX batches with a progress bar instead of the command line.

1.17.2

  • Fixed a WordPress.org submission error: the Plugin URI and Author URI
    headers pointed at the same URL. Author URI now points at the authorÌtumọ̀ Yorùbá: ’s
    GitHub profile instead of the plugin repo.

1.17.1

  • Cleared the remaining WordPress.org Plugin Check warnings: dropped the
    now-redundant load_plugin_textdomain() call (translations for wp.org-hosted
    plugins load automatically since WP 4.6) and annotated the opt-in REST debug
    logger. No functional or tracking changes.

1.17.0

  • WordPress.org compliance release: readme.txt, uninstall.php with opt-in data
    removal, direct-access guards on all files, and the GitHub update checker is
    no longer part of the WordPress.org build (updates come from wordpress.org).

1.16.1

  • Long lists are now collapsed before first paint (CSS), eliminating the
    load-time jump introduced in 1.16.0.

1.16.0

  • Cache offload insight (served-from-cache % with layer detection).
  • Landing pages report with per-landing bounce rate.
  • Blocks with no data for the selected period are hidden entirely.
  • All lists use one in-place Show more / Show less toggle; server-side
    pagination removed.

1.15.0

  • Bilingual: English source strings with a bundled Dutch (nl_NL) translation.

1.14.0

  • Raw (non-deduplicated) view counting, views per visitor, average visit
    duration, an hourly visitors chart for “Today”, and full-URL outbound link
    tracking including the source page.

Earlier

  • See https://github.com/fbloemhof/turf-stats/releases for the full history.