Tinker Stats for Rybbit

Description

Tinker Stats for Rybbit integrates your self-hosted Rybbit Analytics instance with WordPress. It manages your Rybbit tracking script on the frontend and surfaces content traffic stats directly in your admin; post list columns, editor publish box, and dashboard widget.

Tracking Script Management:

  • One-click setup — enter your connection details, click Test Connection, and the tracking script is ready to enable.
  • Built-in proxy — bypass ad blockers with one toggle. The plugin serves the tracking script and forwards events through your own domain. No webserver config needed.
  • Script injection — adds the Rybbit tracking script to your frontend via wp_enqueue_script() with configurable script URL, debounce, and session replay.
  • Role exclusion — exclude specific WordPress roles (e.g. administrators, editors) from being tracked.
  • User identification — optionally send logged-in user ID, display name, and role to Rybbit via rybbit.identify().
  • Custom events — fire per-type events (post_view, page_view, archive_view, search_view, etc.) with WordPress metadata as event properties.
  • Error suppression — suppress cross-origin “Script error.” messages from the tracking script. This is related to a bit of a bug in Rybbit error reporting and merely cleans up session reports.

Admin Content Stats Display:

  • Post list column — see view counts for every post at a glance. Click any count to open a detailed chart.
  • Chart modal — daily breakdown with Year/Month/Week tabs and period navigation. Shows total count with percentage change vs previous period.
  • Dashboard widget — site-wide traffic chart, total count with trend indicator, and top 10 pages.
  • Publish box stat — current month view count right in the editor, clickable to open the chart modal.
  • Postmeta sync — hourly cron job saves view counts to post meta for use by themes, page builders, and other plugins.
  • Configurable metric — choose between Pageviews, Visits (sessions), or Users.

Security & Privacy:

  • Encrypted API key — your Rybbit API key is stored encrypted in the database.
  • Caching — configurable transient caching to minimize API calls to your Rybbit instance.
  • API resilience — automatic backoff on errors prevents cascading failures.
  • View permissions — restrict who can see stats in the admin.
  • Privacy-first — Rybbit is cookieless and GDPR-friendly. No data leaves your infrastructure.

This plugin is not an official Rybbit product and is not affiliated with or endorsed by Rybbit. Rybbit trademarks, logos, and brand assets remain the property of their respective owner.

Requirements:

  • A running self-hosted Rybbit Analytics instance (tested with v2.5.0)
  • A Rybbit API key (generated in Settings Account API Keys)
  • WordPress 6.3 or later

Multisite

The plugin works on WordPress Multisite networks (subdirectory, subdomain, and domain-mapped). The plugin can be activated per-subsite or network-wide; either works. Each subsite has its own Tinker Stats menu and runs its own postmeta sync.

How independently each subsite can be configured depends on Rybbit’s per-domain data model. Subdomain and domain-mapped subsites can each connect to their own Rybbit site. Subdirectory subsites must share the parent domain’s Rybbit configuration (Site ID, Tracking ID, URL, and API key), since Rybbit organizes data per registered domain or subdomain rather than by URL path. The URL patterns section below has the full breakdown.

Rybbit + Multisite URL patterns

Rybbit organizes data per registered domain or subdomain, which affects how multisite installs map to Rybbit sites:

  • Subdomain subsites (e.g. sub.example.com) — each can be added as its own Rybbit site for clean separation.
  • Domain-mapped subsites (e.g. example2.com) — each is added as its own Rybbit site, identical to single-site usage.
  • Subdirectory subsites (e.g. example.com/sub) — Rybbit doesn’t support path-based site separation, so all subdirectory subsites must be configured with the same Rybbit Site ID, Tracking ID, URL, and API key as the parent domain. In Rybbit’s dashboard, all subdirectory subsite traffic appears under the parent domain’s site; use Rybbit’s URL/path filters to view per-subsite breakdowns.

If you proxy the Rybbit script through a relative path (e.g. /api/script.js), make sure the proxy is configured on each subsite’s domain.

Screenshots

  • Post list with Visits column — click any count to open the chart modal
  • Chart modal — daily breakdown with Year/Month/Week tabs and period navigation
  • Dashboard widget — site-wide traffic chart with top pages
  • Publish box — current month view count in the editor
  • Settings page — Connection, Tracking, Display, and Tools sections

Installation

  1. Upload the tinker-stats-for-rybbit folder to /wp-content/plugins/
  2. Activate the plugin through the ‘Plugins’ menu
  3. Go to Tinker Stats in the admin menu (or you’ll be redirected automatically on first activation)
  4. Enter your Rybbit instance URL, Site ID, and API key
  5. Click “Test Connection” to verify and auto-populate the Tracking ID
  6. Enable tracking in the Tracking section to inject the script on your frontend
  7. Choose your preferred display metric (Pageviews, Visits, or Users)

FAQ

What is Rybbit?

Rybbit is an open-source, privacy-friendly web analytics platform — a modern alternative to Google Analytics. It supports self-hosting and tracks visitors without cookies.

Does this plugin add tracking to my site?

Yes, if you enable it. The Tracking section lets you inject the Rybbit tracking script on your frontend with one toggle. You can also configure role exclusion, user identification, and custom events. If tracking is disabled, the plugin only reads existing analytics data from your Rybbit instance via its API.

What does the postmeta sync do?

Every hour, the plugin fetches lifetime cumulative metrics from Rybbit and saves them as post meta (_tinker_stats_views). This makes view counts available to themes, page builders, and sorting queries.

The stored value is a running total from the date your Rybbit site was first created — it doesn’t reset at month boundaries. The plugin auto-detects this start date from your Rybbit instance the first time it syncs; if the connection details change (different Rybbit instance or different Site ID), the start date is re-detected on the next sync.

Can I trigger a manual sync?

Yes. Go to Tinker Stats Settings and click “Sync Postmeta Now” in the Tools section.

Is my API key stored securely?

Yes. The API key is encrypted using AES-256-CBC with keys derived from your WordPress salts before being stored in the database.

My visits aren’t being counted — what’s wrong?

The most common cause is ad blockers (uBlock Origin, Brave’s built-in blocker, etc.) blocking the Rybbit tracking script. Even though Rybbit is privacy-friendly and cookieless, some blocklist maintainers block all analytics scripts indiscriminately. Estimates put the impact between 6% and 60% of visitors depending on your audience’s technical level.

A common gotcha: ad blocker filter lists also match on URL keywords. Words like “stats”, “analytics”, “tracking”, “metrics”, or “telemetry” anywhere in the script URL will trigger blockers regardless of the actual script content. If your Script URL contains any of these, expect blocking even before the request reaches your server.

The fix is to proxy the tracking script through your own domain so it loads as a first-party request. The plugin includes a built-in proxy that handles this for you: go to Tinker Stats Settings Tracking and enable the “Bypass Ad Blockers” toggle. The plugin will fetch and cache the Rybbit script, generate a randomized first-party path, and forward all tracking events through your own domain. No webserver config required.

If you’d rather configure the proxy yourself at the webserver or CDN level, see Rybbit’s proxy guide and enter the relative path (e.g. /api/script.js) in the Script URL field.

To verify your tracking is actually firing, open your Rybbit dashboard, go to Site Settings Tracking Script, and click “Verify Installation”. Rybbit will check whether the script is loading on your site and confirm the connection.

Other less common causes: tracking is disabled in the plugin settings, your role is in the Exclude Roles list, or the tracking ID isn’t populated yet.

How does the built-in proxy work?

When enabled, the plugin generates a randomized 8-character hex prefix (e.g. k3p9m2qr) and serves the Rybbit tracking script and tracking endpoints through https://yoursite.com/[hash]/.... Static script files are cached locally and refreshed daily; tracking POSTs are forwarded to your Rybbit instance in real time, with the visitor’s real IP address preserved via X-Forwarded-For.

The randomized prefix breaks ad blocker rules that anchor on the platform-pattern /api/ path used by analytics services. You can regenerate paths anytime, refresh the script cache manually, and verify proxy reachability with the Test Proxy button.

The proxy works on most hosts without webserver-level changes. On WordPress Multisite, each subsite gets its own proxy paths (subdirectory subsites can share the parent domain’s Rybbit Site ID; subdomain and mapped-domain subsites have their own).

I use page caching — do I need to do anything?

The tracking script is injected into your page HTML via wp_enqueue_script(), so it becomes part of the cached HTML output. This is normally desirable: once cached, the script tag is served on every page hit without any per-request PHP work.

The trade-off: if you enable, disable, or change tracking settings, your cached HTML still reflects the previous state. You’ll need to purge your page cache (FastCGI, Cloudflare, Varnish, etc.) for the change to take effect on the frontend.

The built-in proxy’s cached script files (/tsr-{hash}/script.js etc.) are static files at WordPress root, served directly by your webserver. They’re independent of WordPress page caching: they refresh on their own daily-cron schedule and aren’t affected by page cache purges.

Reviews

There are no reviews for this plugin.

Contributors & Developers

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

Contributors

Changelog

1.4.1.3

  • Fixed: postmeta sync was overwriting _tinker_stats_views with current-calendar-month-only data on each hourly run, causing values to collapse at month boundaries. The cron now queries lifetime cumulative metrics — start date is auto-detected from your Rybbit instance’s site creation date and cached, with sanity bounds and re-detection when connection details change.
  • Refactored: filesystem operations across the proxy cache manager, MU-plugin deployer, and uninstall cleanup migrated to the WP_Filesystem API for WordPress.org coding-standards compliance.
  • Refactored: MU-plugin tracking forwarder migrated from raw cURL to wp_remote_request(). Same timeout, SSL verification, and request shape as before; this is a coding-standards refactor, not a behavior change.
  • Hardened: $_SERVER reads in the deployed MU-plugin now properly unslashed and sanitized.
  • Note: removed the redundant Network: false plugin header line — single-site is the implicit default and only Network: true is a valid value per WordPress.org spec.

1.4.1.1

  • Added: MU-plugin fast path for proxy requests. When the proxy is enabled, the plugin auto-deploys a small MU-plugin to wp-content/mu-plugins/tinker-stats-proxy.php that handles forwarded /track and /site/tracking-config/ requests directly, bypassing the full WordPress bootstrap. Works on single-site and WordPress Multisite (each subsite gets its own per-host config sidecar). Falls back automatically to the existing PHP handler if the mu-plugins directory isn’t writable; the proxy works in either mode.
  • Added: Fast path status row in the proxy fieldset on the Settings page so you can see at a glance whether the MU-plugin path is active or PHP fallback is in use.
  • Added: optional proxy debug logger for diagnosing visitor-IP attribution issues. Enable by adding define( 'TINKER_STATS_PROXY_DEBUG', true ); to wp-config.php. When enabled, each forwarded tracking request writes one line to the PHP error log capturing the inbound IP-related $_SERVER values, the IP the proxy resolved as the visitor’s, and the exact X-Real-IP / X-Forwarded-* headers sent to Rybbit. Off by default; safe to leave in production.
  • Added: page title metadata to the home_view event — homepage hits now carry the page title alongside other custom-event metadata.
  • Added: pre-flight check in the proxy enable flow that verifies WordPress root is writable by PHP. If it isn’t (some hosts lock the document root), enable surfaces a clear error and points to Rybbit’s external proxy guide as the alternative.
  • Fixed: PHP warnings (“Attempt to read property on false”) on archive pages where get_queried_object() failed to resolve — most commonly bot or invalid URL hits to /author/{slug}/ for slugs that don’t match any user. The tracking script now defensively type-checks the queried object on singular, term, and author archive branches before reading properties. Unresolvable archives no longer fire tracking events with garbage metadata.
  • Fixed: visitor IP attribution through the built-in proxy. The plugin now injects the resolved visitor IP into the request body of forwarded /track events. Rybbit’s track endpoint honors this body field directly, bypassing any Cloudflare or nginx layer between the WordPress server and the Rybbit backend that may otherwise overwrite reverse-proxy headers like X-Real-IP. Pageviews, custom events, errors, and outbound clicks now record the real visitor IP regardless of the Rybbit deployment topology. (Note: Rybbit’s /identify and session-replay endpoints don’t currently support this override pattern; an upstream Rybbit change would close that gap.)
  • Fixed: contradicting positioning language. Plugin description and readme requirements now consistently describe Tinker Stats for Rybbit as integration with self-hosted Rybbit instances.
  • Fixed: built-in proxy was forwarding tracking events with only X-Forwarded-For, causing Rybbit to record the WordPress server’s IP instead of the real visitor IP on setups that use X-Real-IP for client identification. Proxy now sets X-Forwarded-For, X-Real-IP, X-Forwarded-Proto, and X-Forwarded-Host, matching the standard reverse-proxy header pattern.
  • Fixed: homepage events with custom event tracking enabled were silently dropped on some self-hosted Rybbit instances. The home_view event was sending an empty properties array ([]) instead of an empty object ({}), which Rybbit’s analytics database rejects. Some setups dropped only the home_view row; others dropped the homepage pageview alongside it.
  • Fixed: built-in proxy returned 404 on managed hosts (xCloud, WP Engine, Kinsta, etc.) where nginx has a static-asset location block that returns 404 directly for missing .js files instead of falling through to PHP. Cached tracking script files now live at WordPress root in a tsr-{hash}/ directory so nginx serves them from disk as static assets. Non-static endpoints (/track, /site/tracking-config/..., etc.) continue to route through PHP under the same prefix.
  • Refactored: outbound request logic factored into a shared helpers class so the MU-plugin fast path and PHP fallback produce byte-identical requests to Rybbit. Same visitor IP attribution, same headers, same body-field injection in both paths.
  • Hardened: event property serialization now forces JSON object output (JSON_FORCE_OBJECT) so any future event with empty metadata can’t reproduce the same failure mode.
  • Note: if you had v1.4.0’s proxy enabled, disable and re-enable after upgrading — the cache directory has moved.

1.4.0

  • Added: built-in proxy feature — bypass ad blockers with one toggle. Plugin serves the Rybbit tracking script and forwards tracking events through your own domain via a randomized first-party path (e.g. /k3p9m2qr/script.js).
  • Added: Settings Tracking “Bypass Ad Blockers” fieldset with proxy enable/disable, paths display, copy-to-clipboard, Refresh Now, Regenerate Paths, and Test Proxy actions.
  • Added: daily cron refresh of cached script.js, replay.js, and metrics.js from your Rybbit instance. Filterable via tinker_stats_proxy_refresh_interval.
  • Added: tracking POST forwarding (/[hash]/track) preserves real visitor IP via X-Forwarded-For (Cloudflare-aware).
  • Added: catch-all forwarder for any future Rybbit endpoint under /api/* — proxy keeps working without plugin update if Rybbit adds new endpoints.
  • Added: cache directory at /wp-content/uploads/tinker-stats-proxy-cache/ survives plugin updates.
  • Added: hash regeneration via Regenerate Paths button — cache files preserved (URL-independent shared dir).
  • Note: this version uses WordPress’s parse_request for routing, which works on all single-site and multisite installs. A faster MU-plugin path for single-site installs is planned for a future release.

1.3.2

  • Added: multisite compatibility — network activation now installs defaults and schedules cron on every existing subsite. New subsites added to a network where the plugin is network-active automatically get defaults and cron set up.
  • Added: lazy cron-schedule fallback ensures the postmeta sync runs on every subsite even if the activation hook missed it.
  • Added: multisite-aware uninstall — cleanup runs on every subsite, no more orphaned options or postmeta.
  • Added: activation redirect — after activating on a single site or per-subsite, you’re sent straight to the Settings page.
  • Added: Settings moved to a top-level menu (Tinker Stats) in preparation for future Events and Stats submenu pages.
  • Added: contextual hint next to Site ID field on multisite subsites, explaining how Rybbit’s domain-based site organization maps to subdirectory/subdomain/mapped subsites.
  • Added: contextual hint next to Script URL field about ad blockers, with a link to Rybbit’s proxy guide.
  • Fixed: “Settings saved” notice not appearing after saving — top-level menus require an explicit notice render call.
  • Fixed: top-level menu position moved into the plugin group (below the separator after Settings) instead of squeezed between Settings and Tools.
  • Documentation: expanded Multisite section in readme with explicit Rybbit URL pattern compatibility.
  • Documentation: new FAQ entry for “My visits aren’t being counted” addressing ad blockers.

1.3.0

  • Initial release.