Title: Turf Stats
Author: Freek
Published: <strong>juillet 8, 2026</strong>
Last modified: septembre 9, 2026

---

Search plugins

![](https://ps.w.org/turf-stats/assets/banner-772x250.png?rev=3600268)

![](https://ps.w.org/turf-stats/assets/icon.svg?rev=3600268)

# Turf Stats

 By [Freek](https://profiles.wordpress.org/freakstar/)

[Download](https://downloads.wordpress.org/plugin/turf-stats.1.26.0.zip)

 * [Details](https://tah.wordpress.org/plugins/turf-stats/#description)
 * [Reviews](https://tah.wordpress.org/plugins/turf-stats/#reviews)
 *  [Installation](https://tah.wordpress.org/plugins/turf-stats/#installation)
 * [Development](https://tah.wordpress.org/plugins/turf-stats/#developers)

 [Support](https://wordpress.org/support/plugin/turf-stats/)

## Description

Turf is a self-hosted, cookieless analytics plugin. No Google Analytics, no
 Jetpack,
no external network calls – 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’s
    header
   when available – 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 – 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, …) – 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.

## Screenshots

[⌊Statistics overview - views, visitors, device/browser/OS breakdowns, cache
offload,
peak hours, and top pages and posts for the selected period.⌉⌊Statistics overview-
views, visitors, device/browser/OS breakdowns, cache
offload, peak hours, and top
pages and posts for the selected period.⌉[

Statistics overview – views, visitors, device/browser/OS breakdowns, cache offload,
peak hours, and top pages and posts for the selected period.

[⌊Analysis - landing pages with bounce rate, on-site search terms
(including zero-
result searches), trending content, and per-author stats.⌉⌊Analysis - landing pages
with bounce rate, on-site search terms
(including zero-result searches), trending
content, and per-author stats.⌉[

Analysis – landing pages with bounce rate, on-site search terms (including zero-
result searches), trending content, and per-author stats.

[⌊Bots & LLMs - crawler and AI-bot visits (Googlebot, GPTBot, ClaudeBot, ...)
tracked
separately from human visitors, with the pages they crawl most.⌉⌊Bots & LLMs - crawler
and AI-bot visits (Googlebot, GPTBot, ClaudeBot, ...)
tracked separately from human
visitors, with the pages they crawl most.⌉[

Bots & LLMs – crawler and AI-bot visits (Googlebot, GPTBot, ClaudeBot, …) tracked
separately from human visitors, with the pages they crawl most.

## Installation

 1. Install and activate the plugin.
 2. That’s it – 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’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 – 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’s numbers lower than Jetpack’s or a raw hit counter’s?

« Views » is deduplicated: a repeat view of the same page by the same visitor
 within
30 minutes doesn’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’s CF-IPCountry request header, if your site is behind
 Cloudflare.
Turf deliberately does not call any geolocation API – 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 – 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’s GitHub repository: https://github.com/fbloemhof/turf-stats/issues

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Turf Stats” is open source software. The following people have contributed to this
plugin.

Contributors

 *   [ Freek ](https://profiles.wordpress.org/freakstar/)

[Translate “Turf Stats” into your language.](https://translate.wordpress.org/projects/wp-plugins/turf-stats)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/turf-stats/), check
out the [SVN repository](https://plugins.svn.wordpress.org/turf-stats/), or subscribe
to the [development log](https://plugins.trac.wordpress.org/log/turf-stats/) by 
[RSS](https://plugins.trac.wordpress.org/log/turf-stats/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.26.0

 * Added « Load time trend » and « Time/visit trend » daily line charts to the
    
   Analysis page, next to the existing peak-hours heatmap.
 * Added a « PWA vs. browser » breakdown to the Statistics page: detects
    whether
   a pageview ran inside an installed PWA (Add to Home Screen / Install app) or 
   a normal browser tab. This is a rough estimate of active PWA usage, not a hard
   install count – browsers don’t report installs to the server.

#### 1.25.2

 * Reverted 1.25.1: « mln » for millions reads fine on Dutch sites too, so
    the «
   k »/ »M » abbreviation is back to a plain, always-on suffix instead of going 
   through translation.

#### 1.25.1

 * Fixed: the abbreviated view/visitor counts added in 1.25.0 always showed
    the
   Dutch « mln » for millions, even on English (and other) sites. The « k »/ »M »
   suffixes are now translatable, so non-Dutch locales show « 1.2M » instead of «
   1,2 mln ».

#### 1.25.0

 * Large view/visitor counts are now shown abbreviated (« 12,3k », « 1,2 mln »)
   
   in the totals tiles, the [turf_stats] shortcode widget, and the view count shown
   to visitors on single posts – so big numbers keep fitting their fixed-width space.
   Tables and CSV exports still show exact figures.
 * Fixed the « Go to date » button showing the Dutch word « Toon » instead of
    «
   Show » for every locale except Dutch.

#### 1.24.0

 * The Statistics page now loads noticeably faster: most boxes (device/
    browser/
   OS/country breakdowns, sources, UTM campaigns, top pages/terms, peak hours, etc.)
   load their data in the background right after the page appears, instead of making
   you wait for every query to finish first.
 * The heaviest stats queries are now cached for a few minutes, so reloading
    the
   page or switching periods no longer repeats the same slow database query every
   time.
 * Visual refresh of the Statistics page: breakdown lists, the totals tiles,
    the
   daily chart, and report tables (top pages, top search terms, 404s, clicks, etc.)
   now share one consistent, more compact look with a hover highlight per row and
   a full-width bar behind each breakdown item instead of a separate small bar –
   including the Bots page’s breakdown, which now matches the rest of the plugin
   instead of using its own older style.
 * New sortable « Views » column on the posts/pages list (and any other tracked
   
   post type), so you can see view counts without leaving the list.
 * Fixed the « From/To » (session routes) and outbound-links tables showing
    their
   second column right-aligned instead of left-aligned.
 * Fixed the « Show more » toggle not working on boxes that load their data in
    
   the background.

#### 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 – 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 – 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’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 » – 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’t a valid sorted .
   mo) – 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 – 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’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’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 –offset/–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) – 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’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) – 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 – 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’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.

## Meta

 *  Version **1.26.0**
 *  Last updated **3 semaines ago**
 *  Active installations **30+**
 *  WordPress version ** 6.0 or higher **
 *  Tested up to **7.1.2**
 *  PHP version ** 7.4 or higher **
 *  Language
 * [English (US)](https://wordpress.org/plugins/turf-stats/)
 * Tags
 * [analytics](https://tah.wordpress.org/plugins/tags/analytics/)[cookieless](https://tah.wordpress.org/plugins/tags/cookieless/)
   [page views](https://tah.wordpress.org/plugins/tags/page-views/)[privacy](https://tah.wordpress.org/plugins/tags/privacy/)
   [statistics](https://tah.wordpress.org/plugins/tags/statistics/)
 *  [Advanced View](https://tah.wordpress.org/plugins/turf-stats/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/turf-stats/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/turf-stats/reviews/)

## Contributors

 *   [ Freek ](https://profiles.wordpress.org/freakstar/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/turf-stats/)