{"id":361489,"date":"2026-09-07T14:47:49","date_gmt":"2026-09-07T14:47:49","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/convertdash-analytics\/"},"modified":"2026-09-24T21:01:14","modified_gmt":"2026-09-24T21:01:14","slug":"convertnow-analytics","status":"publish","type":"plugin","link":"https:\/\/es-ar.wordpress.org\/plugins\/convertnow-analytics\/","author":23273719,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.5.0","stable_tag":"1.5.0","tested":"7.1.2","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"ConvertNow Analytics","header_author":"Prateek Zare","header_description":"Analytics built for people who publish. See which authors, posts and content types earn traffic, computed entirely on your own database. Cookieless, self hosted, no external services, no paywall.","assets_banners_color":"f0f5fd","last_updated":"2026-09-24 21:01:14","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/paypal.me\/prateekzare16","header_plugin_uri":"https:\/\/convertnow.tools\/convertnow-analytics\/","header_author_uri":"https:\/\/profiles.wordpress.org\/prateekzare\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":695,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"prateekzare","date":"2026-09-07 16:36:31","revision":3685285},"1.1.0":{"tag":"1.1.0","author":"prateekzare","date":"2026-09-07 21:04:52","revision":3685597},"1.1.1":{"tag":"1.1.1","author":"prateekzare","date":"2026-09-08 09:02:31","revision":3686249},"1.2.0":{"tag":"1.2.0","author":"prateekzare","date":"2026-09-09 20:08:24","revision":3688952},"1.2.1":{"tag":"1.2.1","author":"prateekzare","date":"2026-09-10 00:48:35","revision":3689155},"1.2.2":{"tag":"1.2.2","author":"prateekzare","date":"2026-09-10 19:27:48","revision":3690398},"1.2.3":{"tag":"1.2.3","author":"prateekzare","date":"2026-09-11 09:24:47","revision":3691116},"1.3.0":{"tag":"1.3.0","author":"prateekzare","date":"2026-09-11 19:44:29","revision":3692024},"1.3.1":{"tag":"1.3.1","author":"prateekzare","date":"2026-09-12 12:18:25","revision":3692668},"1.3.2":{"tag":"1.3.2","author":"prateekzare","date":"2026-09-12 22:35:20","revision":3693172},"1.4.0":{"tag":"1.4.0","author":"prateekzare","date":"2026-09-16 20:23:01","revision":3699212},"1.4.1":{"tag":"1.4.1","author":"prateekzare","date":"2026-09-23 00:17:46","revision":3708279},"1.5.0":{"tag":"1.5.0","author":"prateekzare","date":"2026-09-24 21:01:14","revision":3712008}},"upgrade_notice":{"1.5.0":"<p>Health checks, alerts and site icons. Fixes skipped weekly emails, stops counting signed in editors, counts iCloud Private Relay readers, collects faster. Icons and Apple&#039;s relay list now download in the background, with switches in Settings. Settings and the shared link are kept.<\/p>","1.4.0":"<p>Counts people only: data centre addresses, headless browsers and pages nobody interacted with are refused. Filtered Traffic rebuilt, click capture on, plain permalink and tablet sidebar fixes. Numbers may drop; Filtered at a Glance shows why.<\/p>","1.3.2":"<p>Your numbers will get smaller, and that is the fix. A pageview now has to render on screen and then show a sign of a person before it counts. What is left out is counted by name under Stats, so you can check it. Also stops the bot list refusing real browsers. Nothing recorded is changed.<\/p>","1.2.3":"<p>Fixes four faults that were inflating visitors and views, sometimes by an order of magnitude, and cleans the stored history in the background. Ranked reports now page and search on the server. Recommended for everyone.<\/p>","1.2.2":"<p>Adds an optional anonymous usage report, off by default. You are asked once, shown the exact payload first, and never asked again if you decline. Nothing about your visitors is ever included.<\/p>","1.2.1":"<p>Adds an on demand insights screen under Data that reports what actually moved in the selected period, exportable as CSV or JSON. Also adds an optional longer visitor recognition window for counting returning readers, off by default.<\/p>","1.2.0":"<p>Counts readers without JavaScript, survives a blocked REST route, and no longer drops Do Not Track readers on new installs. A page read now costs one request instead of two, and the beacon backs off when your host returns 429. Recommended if you see rate limiting.<\/p>","1.1.1":"<p>Deleting the plugin now keeps your settings, shared link and history by default, so updating by hand no longer wipes them. The AI screens are scoped to assistant traffic only, and authors, content types, assistants and campaigns can now be filtered on.<\/p>","1.1.0":"<p>Adds AI Visibility reporting and fixes Gemini being counted as organic search. Summaries are rebuilt on upgrade so past days are reclassified too.<\/p>","1.0.0":"<p>First public release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3685592,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3685592,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3698791,"resolution":"1544x500","location":"assets","locale":"","width":1543,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3698791,"resolution":"772x250","location":"assets","locale":"","width":771,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3712008,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"landingPage\":\"\\\/wp-admin\\\/admin.php?page=convertnow-analytics\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"phpExtensionBundles\":[\"kitchen-sink\"],\"features\":{\"networking\":false},\"constants\":{\"WP_DEBUG\":true,\"WP_DEBUG_LOG\":true,\"WP_DEBUG_DISPLAY\":true,\"SCRIPT_DEBUG\":true},\"siteOptions\":{\"blogname\":\"ConvertNow Analytics demo\",\"timezone_string\":\"Europe\\\/Berlin\"},\"steps\":[{\"step\":\"login\",\"username\":\"admin\",\"password\":\"password\"},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"convertnow-analytics\"},\"options\":{\"activate\":true}},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/cnwa-demo-seed.php\",\"data\":\"<?php\\n\\\/**\\n * Demo data for a Playground preview.\\n *\\n * Two tables get filled, not one. Filling only the hits table shows a\\n * dashboard where every Filtered Traffic screen says \\\"nothing recorded\\\",\\n * which is the part of this release somebody previewing it most wants to\\n * look at.\\n *\\n * Rows go in a few hundred at a time rather than one at a time. Playground\\n * runs MySQL compiled to WebAssembly, where two thousand separate INSERT\\n * statements is the difference between a preview that opens and a preview\\n * somebody gives up on.\\n *\\\/\\n\\nglobal $wpdb;\\n\\n$hits     = $wpdb->prefix . 'cnwa_hits';\\n$filtered = $wpdb->prefix . 'cnwa_filtered';\\n$days     = 46;\\n\\n\\\/* ------------------------------------------------------------------ *\\n * Something for the paths to point at\\n * ------------------------------------------------------------------ *\\\/\\n\\n\\\/*\\n * Without this the reports list paths that lead nowhere, and the open in a\\n * new tab link on every row gives a 404. Nine posts and pages, with the\\n * permalink structure to match, so the demo behaves like a site.\\n *\\\/\\nif ( ! defined( 'CNWA_DEMO_DRY_RUN' ) ) {\\n\\tglobal $wp_rewrite;\\n\\tupdate_option( 'permalink_structure', '\\\/blog\\\/%postname%\\\/' );\\n\\tif ( isset( $wp_rewrite ) && is_object( $wp_rewrite ) ) {\\n\\t\\t$wp_rewrite->init();\\n\\t\\t$wp_rewrite->flush_rules();\\n\\t}\\n\\n\\t$tools_id = wp_insert_post(\\n\\t\\tarray(\\n\\t\\t\\t'post_type'    => 'page',\\n\\t\\t\\t'post_status'  => 'publish',\\n\\t\\t\\t'post_title'   => 'Tools',\\n\\t\\t\\t'post_name'    => 'tools',\\n\\t\\t\\t'post_content' => 'Small browser based utilities.',\\n\\t\\t)\\n\\t);\\n\\n\\t$content = array(\\n\\t\\tarray( 'page', 'About', 'about', 0 ),\\n\\t\\tarray( 'page', 'Pricing', 'pricing', 0 ),\\n\\t\\tarray( 'page', 'Contact', 'contact', 0 ),\\n\\t\\tarray( 'page', 'Merge PDFs', 'pdf-merge', $tools_id ),\\n\\t\\tarray( 'page', 'CSV to JSON', 'csv-to-json', $tools_id ),\\n\\t\\tarray( 'post', 'Why self hosted analytics', 'why-self-hosted-analytics', 0 ),\\n\\t\\tarray( 'post', 'AI referrals explained', 'ai-referrals-explained', 0 ),\\n\\t\\tarray( 'post', 'What counts as a visitor', 'what-counts-as-a-visitor', 0 ),\\n\\t);\\n\\n\\tforeach ( $content as $c ) {\\n\\t\\twp_insert_post(\\n\\t\\t\\tarray(\\n\\t\\t\\t\\t'post_type'    => $c[0],\\n\\t\\t\\t\\t'post_status'  => 'publish',\\n\\t\\t\\t\\t'post_title'   => $c[1],\\n\\t\\t\\t\\t'post_name'    => $c[2],\\n\\t\\t\\t\\t'post_parent'  => $c[3],\\n\\t\\t\\t\\t'post_author'  => 1,\\n\\t\\t\\t\\t'post_date_gmt' => gmdate( 'Y-m-d H:i:s', time() - random_int( 40, 300 ) * DAY_IN_SECONDS ),\\n\\t\\t\\t\\t'post_content' => 'Demo content for the ConvertNow Analytics preview.',\\n\\t\\t\\t)\\n\\t\\t);\\n\\t}\\n}\\n\\n\\\/* ------------------------------------------------------------------ *\\n * Readers\\n * ------------------------------------------------------------------ *\\\/\\n\\n$pages = array(\\n\\tarray( '\\\/', 'Home', 'page' ),\\n\\tarray( '\\\/about', 'About', 'page' ),\\n\\tarray( '\\\/blog\\\/why-self-hosted-analytics', 'Why self hosted analytics', 'post' ),\\n\\tarray( '\\\/blog\\\/ai-referrals-explained', 'AI referrals explained', 'post' ),\\n\\tarray( '\\\/blog\\\/what-counts-as-a-visitor', 'What counts as a visitor', 'post' ),\\n\\tarray( '\\\/tools\\\/pdf-merge', 'Merge PDFs', 'page' ),\\n\\tarray( '\\\/tools\\\/csv-to-json', 'CSV to JSON', 'page' ),\\n\\tarray( '\\\/pricing', 'Pricing', 'page' ),\\n\\tarray( '\\\/contact', 'Contact', 'page' ),\\n);\\n\\n$referrers = array(\\n\\tnull, null, null,\\n\\t'google.com', 'google.com', 'google.com',\\n\\t'bing.com', 'duckduckgo.com',\\n\\t'chatgpt.com', 'chatgpt.com', 'perplexity.ai', 'claude.ai', 'gemini.google.com',\\n\\t'news.ycombinator.com', 'x.com', 'reddit.com', 'linkedin.com',\\n);\\n\\n\\\/*\\n * Browser, version, system, device and screen travel together, because they\\n * do in life. The first version of this seed paired Safari with a mobile\\n * device on macOS and Firefox with a desktop device on iOS, and the Devices\\n * report showed macOS phones.\\n *\\\/\\n$clients = array(\\n\\tarray( 'Chrome', '126', 'Windows 10\\\/11', 'desktop', '1920x1080' ),\\n\\tarray( 'Chrome', '126', 'macOS', 'desktop', '2560x1440' ),\\n\\tarray( 'Safari', '17', 'macOS', 'desktop', '1512x982' ),\\n\\tarray( 'Safari', '17', 'iOS', 'mobile', '390x844' ),\\n\\tarray( 'Chrome', '126', 'Android 14', 'mobile', '412x915' ),\\n\\tarray( 'Firefox', '127', 'Windows 10\\\/11', 'desktop', '1920x1080' ),\\n\\tarray( 'Firefox', '127', 'Ubuntu', 'desktop', '1920x1080' ),\\n\\tarray( 'Edge', '126', 'Windows 10\\\/11', 'desktop', '1536x864' ),\\n\\tarray( 'Samsung Internet', '25', 'Android 14', 'mobile', '412x915' ),\\n\\tarray( 'Safari', '17', 'iOS', 'tablet', '1024x1366' ),\\n);\\n\\n$countries = array( 'DE', 'DE', 'US', 'US', 'IN', 'GB', 'FR', 'NL', 'CA', 'ES', 'AU', 'BR' );\\n$languages = array( 'de-de', 'en-gb', 'en-us', 'en-us', 'fr-fr', 'nl-nl', 'es-es' );\\n\\n$batch = array();\\n$rows  = 0;\\n\\nfor ( $d = $days - 1; $d >= 0; $d-- ) {\\n\\t\\\/\\\/ A weekly rhythm, a slow climb, and a quieter weekend.\\n\\t$weekday = (int) gmdate( 'N', time() - $d * DAY_IN_SECONDS );\\n\\t$base    = 34 + (int) round( 18 * sin( $d \\\/ 7.0 ) ) + (int) round( ( $days - $d ) * 0.35 );\\n\\t$per     = max( 8, $weekday >= 6 ? (int) round( $base * 0.6 ) : $base );\\n\\n\\t$visitors = max( 3, (int) ceil( $per \\\/ 2.6 ) );\\n\\n\\tfor ( $i = 0; $i < $per; $i++ ) {\\n\\t\\t$group = $i % $visitors;\\n\\n\\t\\t\\\/\\\/ The identifier is rebuilt nightly in the plugin, so the demo data\\n\\t\\t\\\/\\\/ is built the same way: a visitor id belongs to one day only.\\n\\t\\t$hash = substr( md5( 'cnwa-demo-visitor-' . $d . '-' . $group ), 0, 32 );\\n\\t\\t$sess = substr( md5( 'cnwa-demo-visit-' . $d . '-' . $group ), 0, 16 );\\n\\n\\t\\t$page   = $pages[ ( $group + $i ) % count( $pages ) ];\\n\\t\\t$client = $clients[ ( $group * 3 + $i ) % count( $clients ) ];\\n\\t\\t$ref    = $referrers[ ( $group * 5 + $i ) % count( $referrers ) ];\\n\\n\\t\\t$campaign = ( 0 === ( $group + $i ) % 17 );\\n\\n\\t\\t\\\/\\\/ Landing views open a visit; later views in the same visit sit a few\\n\\t\\t\\\/\\\/ minutes after it, so visits hold together and bounce rate is real.\\n\\t\\t$start = time() - $d * DAY_IN_SECONDS - ( ( $group * 977 ) % 86400 );\\n\\t\\t$when  = $start + ( intdiv( $i, $visitors ) * random_int( 40, 400 ) );\\n\\t\\tif ( $when > time() ) {\\n\\t\\t\\t$when = time() - random_int( 60, 3600 );\\n\\t\\t}\\n\\n\\t\\t$batch[] = $wpdb->prepare(\\n\\t\\t\\t'(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%d,%d,%s)',\\n\\t\\t\\tsubstr( bin2hex( random_bytes( 8 ) ), 0, 16 ),\\n\\t\\t\\t$sess,\\n\\t\\t\\t$hash,\\n\\t\\t\\t$page[0],\\n\\t\\t\\t$page[1],\\n\\t\\t\\t$ref,\\n\\t\\t\\tnull,\\n\\t\\t\\t$campaign ? 'newsletter' : null,\\n\\t\\t\\t$campaign ? 'email' : null,\\n\\t\\t\\t$campaign ? 'spring-launch' : null,\\n\\t\\t\\t$client[0],\\n\\t\\t\\t$client[1],\\n\\t\\t\\t$client[2],\\n\\t\\t\\t$client[3],\\n\\t\\t\\t$client[4],\\n\\t\\t\\t$languages[ ( $group + $i ) % count( $languages ) ],\\n\\t\\t\\t$countries[ ( $group * 2 + $i ) % count( $countries ) ],\\n\\t\\t\\t$page[2],\\n\\t\\t\\t1,\\n\\t\\t\\trandom_int( 3, 420 ),\\n\\t\\t\\trandom_int( 8, 100 ),\\n\\t\\t\\tgmdate( 'Y-m-d H:i:s', $when )\\n\\t\\t);\\n\\t\\t++$rows;\\n\\n\\t\\tif ( count( $batch ) >= 250 ) {\\n\\t\\t\\tcnwa_demo_flush_hits( $hits, $batch );\\n\\t\\t}\\n\\t}\\n}\\ncnwa_demo_flush_hits( $hits, $batch );\\n\\nfunction cnwa_demo_flush_hits( $table, &$batch ) {\\n\\tglobal $wpdb;\\n\\tif ( ! $batch ) {\\n\\t\\treturn;\\n\\t}\\n\\t$wpdb->query(\\n\\t\\t\\\"INSERT IGNORE INTO {$table}\\n\\t\\t (view_id, session_id, visitor_hash, url_path, page_title, referrer_domain, referrer_path,\\n\\t\\t  utm_source, utm_medium, utm_campaign, browser, browser_version, os, device, screen,\\n\\t\\t  lang, country, content_type, author_id, duration, scroll, created_at)\\n\\t\\t VALUES \\\" . implode( ',', $batch )\\n\\t);\\n\\t$batch = array();\\n}\\n\\n\\\/* ------------------------------------------------------------------ *\\n * Everything that was refused\\n * ------------------------------------------------------------------ *\\\/\\n\\n\\\/*\\n * Names and kinds are taken from the plugin's own signature table rather\\n * than invented, so every row lands on a real label with its own mark and\\n * its operator's colour on the Filtered Traffic screens.\\n *\\\/\\n$machines = array(\\n\\tarray( 'ai_crawler', 'GPTBot', 26, 3.2 ),\\n\\tarray( 'ai_crawler', 'ClaudeBot', 18, 2.8 ),\\n\\tarray( 'ai_crawler', 'PerplexityBot', 11, 2.1 ),\\n\\tarray( 'ai_crawler', 'Bytespider', 14, 1.4 ),\\n\\tarray( 'ai_crawler', 'Common Crawl', 6, 1.0 ),\\n\\tarray( 'ai_crawler', 'Google Extended', 9, 2.4 ),\\n\\tarray( 'ai_crawler', 'Amazonbot', 7, 1.2 ),\\n\\tarray( 'ai_crawler', 'Meta AI crawler', 5, 1.6 ),\\n\\tarray( 'ai_agent', 'ChatGPT (user fetch)', 12, 3.6 ),\\n\\tarray( 'ai_agent', 'ChatGPT Search', 8, 3.1 ),\\n\\tarray( 'ai_agent', 'Perplexity (user fetch)', 6, 2.7 ),\\n\\tarray( 'ai_agent', 'Claude (user fetch)', 4, 2.9 ),\\n\\tarray( 'ai_agent', 'DuckAssist', 3, 1.5 ),\\n\\tarray( 'search', 'Googlebot', 31, 1.1 ),\\n\\tarray( 'search', 'Bingbot', 16, 1.0 ),\\n\\tarray( 'search', 'Applebot', 5, 1.0 ),\\n\\tarray( 'search', 'Yandex', 4, 1.0 ),\\n\\tarray( 'search', 'PetalBot', 6, 1.0 ),\\n\\tarray( 'seo', 'Ahrefs', 13, 1.0 ),\\n\\tarray( 'seo', 'Semrush', 10, 1.0 ),\\n\\tarray( 'seo', 'Majestic', 5, 1.0 ),\\n\\tarray( 'seo', 'Moz', 4, 1.0 ),\\n\\tarray( 'seo', 'Other SEO crawler', 7, 1.0 ),\\n\\tarray( 'social', 'Facebook', 6, 1.0 ),\\n\\tarray( 'social', 'X', 4, 1.0 ),\\n\\tarray( 'social', 'LinkedIn', 3, 1.0 ),\\n\\tarray( 'social', 'Slack', 5, 1.0 ),\\n\\tarray( 'social', 'WhatsApp', 3, 1.0 ),\\n\\tarray( 'monitor', 'Uptime monitor', 9, 1.0 ),\\n\\tarray( 'monitor', 'Lighthouse', 2, 1.0 ),\\n\\tarray( 'monitor', 'Security scanner', 4, 1.0 ),\\n\\tarray( 'monitor', 'Platform agent', 3, 1.0 ),\\n\\tarray( 'library', 'curl', 5, 1.0 ),\\n\\tarray( 'library', 'Python', 7, 1.0 ),\\n\\tarray( 'library', 'Go', 3, 1.0 ),\\n\\tarray( 'library', 'Sends no browser headers', 6, 1.0 ),\\n\\tarray( 'headless', 'Headless Chrome', 8, 1.9 ),\\n\\tarray( 'headless', 'Automation stack', 3, 1.4 ),\\n\\tarray( 'prefetch', 'Prefetch', 11, 1.0 ),\\n\\tarray( 'prefetch', 'Prerender', 4, 1.0 ),\\n\\tarray( 'no_signal', 'No interaction or dwell', 15, 1.0 ),\\n\\tarray( 'no_signal', 'Page never rendered', 9, 1.0 ),\\n\\tarray( 'no_signal', 'Page never shown', 6, 1.0 ),\\n\\tarray( 'unverified', 'No proof of a reader', 5, 1.0 ),\\n\\tarray( 'excluded', 'Excluded role', 4, 1.0 ),\\n\\tarray( 'other', 'Unnamed crawler', 8, 1.0 ),\\n\\tarray( 'flood', 'Too many requests', 2, 1.0 ),\\n);\\n\\n$batch = array();\\n$refused = 0;\\n\\nfor ( $d = $days - 1; $d >= 0; $d-- ) {\\n\\t$date = gmdate( 'Y-m-d', time() - $d * DAY_IN_SECONDS );\\n\\n\\t\\\/\\\/ The AI crawlers climb over the last fortnight, which is what the last\\n\\t\\\/\\\/ two years have actually looked like for most sites.\\n\\t$ai_wave = ( $d < 14 ) ? ( 1 + ( 14 - $d ) * 0.16 ) : 1.0;\\n\\n\\tforeach ( $machines as $m ) {\\n\\t\\tlist( $kind, $label, $typical, $ai_factor ) = $m;\\n\\n\\t\\t$multiplier = in_array( $kind, array( 'ai_crawler', 'ai_agent' ), true ) ? $ai_wave : 1.0;\\n\\t\\t\\\/*\\n\\t\\t * Scaled so refused traffic lands at roughly three times counted\\n\\t\\t * traffic, which is what a small site with a tools section actually\\n\\t\\t * sees. Left unscaled it reads as eleven times, which is true of some\\n\\t\\t * sites and makes a demo look like a honeypot.\\n\\t\\t *\\\/\\n\\t\\t$hits_today = (int) round( $typical * 0.34 * $multiplier * ( 0.6 + ( random_int( 0, 80 ) \\\/ 100 ) ) );\\n\\n\\t\\tif ( $hits_today < 1 ) {\\n\\t\\t\\tcontinue;\\n\\t\\t}\\n\\n\\t\\t$batch[] = $wpdb->prepare( '(%s,%s,%s,%d)', $date, $kind, $label, $hits_today );\\n\\t\\t$refused += $hits_today;\\n\\n\\t\\tif ( count( $batch ) >= 250 ) {\\n\\t\\t\\tcnwa_demo_flush_filtered( $filtered, $batch );\\n\\t\\t}\\n\\t}\\n}\\ncnwa_demo_flush_filtered( $filtered, $batch );\\n\\nfunction cnwa_demo_flush_filtered( $table, &$batch ) {\\n\\tglobal $wpdb;\\n\\tif ( ! $batch ) {\\n\\t\\treturn;\\n\\t}\\n\\t$wpdb->query(\\n\\t\\t\\\"INSERT INTO {$table} (stat_date, kind, label, hits) VALUES \\\"\\n\\t\\t. implode( ',', $batch )\\n\\t\\t. ' ON DUPLICATE KEY UPDATE hits = hits + VALUES(hits)'\\n\\t);\\n\\t$batch = array();\\n}\\n\\n\\\/* ------------------------------------------------------------------ *\\n * Summarise, so the dashboard reads the way it would on a live site\\n * ------------------------------------------------------------------ *\\\/\\n\\nif ( class_exists( 'CNWA_Rollup' ) && ! defined( 'CNWA_DEMO_DRY_RUN' ) ) {\\n\\tCNWA_Rollup::run( $days + 5 );\\n}\\n\\n\\\/\\\/ The upgrade notices belong to a site that upgraded. This one is new.\\ndelete_option( 'cnwa_gate_notice' );\\ndelete_option( 'cnwa_repair_pending' );\\ndelete_option( 'cnwa_classify_pending' );\\ndelete_option( 'cnwa_noscript_notice' );\\nupdate_option( 'cnwa_installed_on', gmdate( 'Y-m-d', time() - $days * DAY_IN_SECONDS ), false );\\n\\necho 'Seeded ' . (int) $rows . ' pageviews and ' . (int) $refused . ' refused requests across ' . (int) $days . ' days.';\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php require_once '\\\/wordpress\\\/wp-load.php'; require '\\\/wordpress\\\/cnwa-demo-seed.php';\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0","1.1.1","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.5.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3698359,"resolution":"1","location":"assets","locale":"","width":1254,"height":1400},"screenshot-10.png":{"filename":"screenshot-10.png","revision":3698359,"resolution":"10","location":"assets","locale":"","width":1254,"height":1250},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3698359,"resolution":"2","location":"assets","locale":"","width":1254,"height":1150},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3698359,"resolution":"3","location":"assets","locale":"","width":1254,"height":1200},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3698359,"resolution":"4","location":"assets","locale":"","width":1254,"height":1250},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3698359,"resolution":"5","location":"assets","locale":"","width":1254,"height":1150},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3698359,"resolution":"6","location":"assets","locale":"","width":1254,"height":1250},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3698359,"resolution":"7","location":"assets","locale":"","width":1254,"height":1150},"screenshot-8.png":{"filename":"screenshot-8.png","revision":3698359,"resolution":"8","location":"assets","locale":"","width":1254,"height":1200},"screenshot-9.png":{"filename":"screenshot-9.png","revision":3698359,"resolution":"9","location":"assets","locale":"","width":1254,"height":1150}},"screenshots":{"1":"The Overview: visitors, visits, views, bounce rate and time on page, with the chart's optional trend, average, unusual-day and forecast layers switched on","2":"Filtered at a Glance: people against machines, what was refused and why, when machines come, and the pages they fetch most","3":"AI at a Glance: which assistants send people, what they were sent to, and the pages the models are reading","4":"Proof of Presence: every counted view grouped by what proved a person was there","5":"Pages at a Glance: what gets read, where visits begin and where they end","6":"Referrers at a Glance: channels, search engines and social networks together","7":"Campaigns at a Glance: UTM sources, mediums and campaigns in one place","8":"Geographic at a Glance: countries and languages, with a world map","9":"Goals, with the builder open: each rule reads back in a sentence and says how many of your pages it matches","10":"Settings: the presence rule, the bot filter level, data retention and the weekly summary email"}},"plugin_section":[262246],"plugin_tags":[256047,232,22770,20315,396],"plugin_category":[36,54],"plugin_contributors":[279589],"plugin_business_model":[],"class_list":["post-361489","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-ai-traffic","plugin_tags-analytics","plugin_tags-bot-detection","plugin_tags-cookieless","plugin_tags-privacy","plugin_category-analytics","plugin_category-security-and-spam-protection","plugin_contributors-prateekzare","plugin_committers-prateekzare"],"banners":{"banner":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/banner-772x250.png?rev=3698791","banner_2x":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/banner-1544x500.png?rev=3698791","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/icon-128x128.png?rev=3685592","icon_2x":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/icon-256x256.png?rev=3685592","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-1.png?rev=3698359","caption":"The Overview: visitors, visits, views, bounce rate and time on page, with the chart's optional trend, average, unusual-day and forecast layers switched on"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-2.png?rev=3698359","caption":"Filtered at a Glance: people against machines, what was refused and why, when machines come, and the pages they fetch most"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-3.png?rev=3698359","caption":"AI at a Glance: which assistants send people, what they were sent to, and the pages the models are reading"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-4.png?rev=3698359","caption":"Proof of Presence: every counted view grouped by what proved a person was there"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-5.png?rev=3698359","caption":"Pages at a Glance: what gets read, where visits begin and where they end"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-6.png?rev=3698359","caption":"Referrers at a Glance: channels, search engines and social networks together"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-7.png?rev=3698359","caption":"Campaigns at a Glance: UTM sources, mediums and campaigns in one place"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-8.png?rev=3698359","caption":"Geographic at a Glance: countries and languages, with a world map"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-9.png?rev=3698359","caption":"Goals, with the builder open: each rule reads back in a sentence and says how many of your pages it matches"},{"src":"https:\/\/ps.w.org\/convertnow-analytics\/assets\/screenshot-10.png?rev=3698359","caption":"Settings: the presence rule, the bot filter level, data retention and the weekly summary email"}],"raw_content":"<!--section=description-->\n<p>Most analytics tools count anything that runs JavaScript, so a self hosted counter can report several times the traffic a site really has. Screenshot services, uptime checks, SEO auditors and a growing crowd of AI agents all run a full browser now.\nConvertNow counts people. A pageview has to render on screen, then somebody has to move, scroll, tap, or stay for two seconds. It also has to come from somewhere people actually read from, not a cloud data center. Everything refused is kept, named, and shown to you under Filtered Traffic, never quietly dropped.<\/p>\n\n<p><strong>AI and machine traffic, measured, not mixed in<\/strong><\/p>\n\n<p>Nobody else treats this as the main event. ConvertNow gives you a chart of people against machines, the pages GPTBot, ClaudeBot and the other crawlers read most, the assistants that actually send readers to you, what every refused request was and when it came, and a Proof of Presence report that shows what convinced the plugin each counted view was real. A crawler is never a visitor, and an assistant fetching a page on somebody's behalf is interest, not attendance.<\/p>\n\n<p><strong>Everything else you expect<\/strong><\/p>\n\n<p>Visitors, visits, views, real bounce rate and time on page. Pages, entry and exit pages, referrers, channels, campaigns, countries, languages, browsers, devices. Goals, funnels, journeys, retention, segments and cohorts. Traffic by author and content type. WooCommerce and Easy Digital Downloads revenue attributed to the visit behind it. A weekly email, a shareable read only dashboard, CSV and JSON export.<\/p>\n\n<p><strong>Runs on your own database<\/strong><\/p>\n\n<p>No account, no key, no tier held back. The tracker inlines into the footer at about 1.5 KB. Raw hits roll up into daily summaries hourly, so a year of history costs the same queries as a day. Every chart is plain SVG.<\/p>\n\n<p><strong>Private by design<\/strong><\/p>\n\n<p>No cookies, no localStorage, no persistent identifier. Visitors are a one way hash of IP, user agent and a salt that rotates daily, so nobody is followed past midnight. Raw IP addresses are never stored. Refused requests are stored as counters only: a date, an hour, a kind, a published name, a path on your site. Never an address.<\/p>\n\n<p><strong>Links<\/strong><\/p>\n\n<ul>\n<li><a href=\"https:\/\/convertnow.tools\/convertnow-analytics\/\">convertnow.tools\/convertnow-analytics<\/a> for docs and screenshots<\/li>\n<li><a href=\"mailto:contact@convertnow.tools\">contact@convertnow.tools<\/a> or <a href=\"mailto:prateekzare@gmail.com\">prateekzare@gmail.com<\/a>, both reach me<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>Tracking, reporting, goals, funnels, journeys, retention, segments, cohorts, insights, alerts, country lookups and exports all run on your own server against your own database. There is no activation call, no licence check and no key. Nothing about your visitors ever leaves your site under any setting. The plugin makes the background requests below, and each one can be switched off.<\/p>\n\n<p><strong>Apple iCloud Private Relay exit list (on by default)<\/strong><\/p>\n\n<p>Once a week the plugin downloads <code>https:\/\/mask-api.icloud.com\/egress-ip-ranges.csv<\/code>, the list Apple publishes of the addresses Private Relay traffic leaves from. It is used only so that Safari readers using Private Relay are not refused as data centre traffic. The request sends nothing but your server's own address, the way any download does. Switch it off in Settings, Tracking, Data centre addresses. Apple's terms: <a href=\"https:\/\/www.apple.com\/legal\/internet-services\/terms\/site.html\">apple.com\/legal\/internet-services\/terms\/site.html<\/a>. Apple's privacy policy: <a href=\"https:\/\/www.apple.com\/legal\/privacy\/\">apple.com\/legal\/privacy<\/a>.<\/p>\n\n<p><strong>Site icons (on by default)<\/strong><\/p>\n\n<p>To show a referring site's icon beside its name, the plugin fetches that icon in the background from the site's own address (its home page and <code>\/favicon.ico<\/code>). When a site refuses requests from servers, the icon is fetched from DuckDuckGo's public icon service at <code>https:\/\/icons.duckduckgo.com\/ip3\/<\/code>, which is told the site's host name and nothing else. Icons are stored under uploads and served from your own site; opening the dashboard requests nothing from anybody else. Switch it off in Settings, Display, Site icons, or keep the icons and stop only the DuckDuckGo fallback with the <code>cnwa_icon_fallback_service<\/code> filter. DuckDuckGo's terms: <a href=\"https:\/\/duckduckgo.com\/terms\">duckduckgo.com\/terms<\/a>. DuckDuckGo's privacy policy: <a href=\"https:\/\/duckduckgo.com\/privacy\">duckduckgo.com\/privacy<\/a>.<\/p>\n\n<p><strong>Alert webhook (off by default)<\/strong><\/p>\n\n<p>If you enter a webhook address under Settings, Alerts, each alert is posted to that address, and only to that address. The request carries the alert's title and sentence, your site's address and a link to the dashboard. Where it goes is the service you chose, under that service's own terms.<\/p>\n\n<p><strong>Anonymous usage reporting (off by default)<\/strong><\/p>\n\n<p>If, and only if, you switch this on, the plugin sends one report a month to convertnow.tools, the author's site, at <code>https:\/\/convertnow.tools\/wp-json\/cnwa-hub\/v1\/report<\/code>. It exists so the author can see which versions are in use and how the plugin is actually being used, and it is used for nothing else.<\/p>\n\n<p>It is off on every install. You are asked once, on the plugin's own screen, with the exact payload from your site shown to you before you decide. Declining is remembered permanently. If you never answer, nothing is ever sent. You can switch it on or off at any time in Settings, Data retention, Anonymous usage report, and switching it off removes the scheduled event immediately.<\/p>\n\n<p>This is the complete payload, and there is nothing else in it:<\/p>\n\n<ul>\n<li>Your site address and site name<\/li>\n<li>The date this plugin was first activated on your site<\/li>\n<li>Your total pageviews and total visitors for the last 30 days, as two numbers<\/li>\n<li>The ConvertNow, WordPress and PHP versions, and your site locale<\/li>\n<\/ul>\n\n<p>It contains nothing about your visitors: no page paths, no referrers, no IP addresses, no visitor hashes, no user accounts, no email addresses, no post or page titles. The request is sent once every 30 days, non blocking, and a failure is silent.<\/p>\n\n<p>Terms of service: <a href=\"https:\/\/convertnow.tools\/terms\/\">convertnow.tools\/terms<\/a>. Privacy policy: <a href=\"https:\/\/convertnow.tools\/privacy\/\">convertnow.tools\/privacy<\/a>.<\/p>\n\n<p><strong>Requests to your own site<\/strong><\/p>\n\n<p>The daily health check loads your own home page and posts a test beacon to your own collector, both at your own address. Nothing leaves your server.<\/p>\n\n<p>The country lookup tables ship inside the plugin as plain text files and are read from disk. There is nothing to download and nothing to import.<strong>Links<\/strong><\/p>\n\n<ul>\n<li>Plugin home page: <a href=\"https:\/\/convertnow.tools\/convertnow-analytics\/\">convertnow.tools\/convertnow-analytics<\/a><\/li>\n<li>Support by email: <a href=\"mailto:contact@convertnow.tools\">contact@convertnow.tools<\/a><\/li>\n<li>The author directly: <a href=\"mailto:prateekzare@gmail.com\">prateekzare@gmail.com<\/a><\/li>\n<li>Support forum: <a href=\"https:\/\/wordpress.org\/support\/plugin\/convertnow-analytics\/\">wordpress.org\/support\/plugin\/convertnow-analytics<\/a><\/li>\n<li>Leave a review: <a href=\"https:\/\/wordpress.org\/support\/plugin\/convertnow-analytics\/reviews\/\">wordpress.org\/support\/plugin\/convertnow-analytics\/reviews<\/a><\/li>\n<\/ul>\n\n<p>Both addresses reach the same inbox. Email is usually the faster of the two; the forum is the better place for anything another user might hit as well.<\/p>\n\n<p><strong>Bundled data<\/strong><\/p>\n\n<p>IP Geolocation by <a href=\"https:\/\/db-ip.com\">DB-IP<\/a>, used under <a href=\"https:\/\/creativecommons.org\/licenses\/by\/4.0\/\">CC BY 4.0<\/a>.<\/p>\n\n<p>Data centre detection reads two plain text tables built from the <a href=\"https:\/\/github.com\/X4BNet\/lists_vpn\">X4BNet datacenter lists<\/a>, MIT licensed, plus a short supplement of documented cloud supernets: <code>assets\/data\/dc-ipv4.csv<\/code> and <code>assets\/data\/dc-ipv6.csv<\/code>, in the same boundary format as the country tables. The build script is <code>tools\/build-dc-tables.py<\/code> in the development sources.<\/p>\n\n<p>Country lookups read two plain text tables built from the DB-IP IP to Country Lite database: <code>assets\/data\/geo-ipv4.csv<\/code> and <code>assets\/data\/geo-ipv6.csv<\/code>. Every line is one boundary, written as an uppercase hex address prefix and an ISO 3166-1 alpha-2 country code, so both files can be opened and read in any text editor. The world map outlines are plain JSON path data at <code>assets\/data\/world.json<\/code>. Nothing in the plugin is compiled, minified or obfuscated.<\/p>\n\n<!--section=installation-->\n<p>From your dashboard:<\/p>\n\n<ol>\n<li>Go to <strong>Plugins \u2192 Add New<\/strong> and search for <strong>ConvertNow Analytics<\/strong>.<\/li>\n<li>Click Install Now, then Activate.<\/li>\n<li>Open <strong>ConvertNow<\/strong> in the admin menu. Data starts appearing immediately.<\/li>\n<\/ol>\n\n<p>Or by hand:<\/p>\n\n<ol>\n<li>Upload the <code>convertnow-analytics<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate it from the Plugins screen.<\/li>\n<li>Open <strong>ConvertNow<\/strong> in the admin menu.<\/li>\n<\/ol>\n\n<p>Either way that is it. No key, no account, no configuration. Country data is built in, so the map and the Countries panel fill in on their own. Your own visits are not counted while you are logged in as an administrator or editor, which is why the numbers stay at zero until real traffic arrives.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20an%20account%20or%20a%20licence%20key%3F\"><h3>Do I need an account or a licence key?<\/h3><\/dt>\n<dd><p>No, and there is nowhere to put one. There is no account, no key, no activation and no paid tier. Nothing about your visitors ever leaves your site. The few background requests the plugin does make, for site icons and Apple's Private Relay list, are listed under External services below, and each has its own switch.<\/p><\/dd>\n<dt id=\"why%20are%20my%20numbers%20lower%20than%20jetpack%2C%20google%20analytics%20or%20my%20host%27s%20stats%3F\"><h3>Why are my numbers lower than Jetpack, Google Analytics or my host's stats?<\/h3><\/dt>\n<dd><p>Four ordinary reasons, in the order they usually matter.<\/p>\n\n<ol>\n<li><strong>You installed it partway through the period.<\/strong> Days before the install are empty because nothing was recorded then, not because they were quiet.<\/li>\n<li><strong>Administrators and editors are not counted<\/strong>, by default. Your own visits, and your team's, are missing on purpose. Settings \u2192 Tracking changes it.<\/li>\n<li><strong>Do Not Track.<\/strong> If that setting is on, those readers are dropped entirely, and most other tools ignore the signal. New installs default to off; a site upgrading from an earlier version keeps it on until changed.<\/li>\n<li><strong>Bots.<\/strong> ConvertNow refuses crawlers, monitors, previewers and AI training bots before a row is written. Some tools count a share of them, which raises their numbers rather than lowering yours.<\/li>\n<li><strong>A pageview needs a person.<\/strong> The page has to render on screen and then somebody has to move, scroll, tap, type, or stay on it for two seconds. Anything that fetched the page and ran the script without a person behind it is not counted, and a good deal of what reads the web does exactly that. Filtered Traffic shows you exactly what was left out, so a low number can be checked rather than believed.<\/li>\n<\/ol>\n\n<p>If the gap still looks wrong after that, the usual cause is the beacon being blocked: a security plugin that hardens <code>\/wp-json\/<\/code>, or a firewall rule. Since 1.2.0 the plugin retries on a front end URL when that happens, and counts readers with no JavaScript at all.<\/p><\/dd>\n<dt id=\"will%20data%20centre%20filtering%20drop%20my%20vpn%20readers%3F\"><h3>Will data centre filtering drop my VPN readers?<\/h3><\/dt>\n<dd><p>Some of them. A VPN whose exit sits in a cloud range looks the same as a headless browser on that range, and the plugin cannot tell the two apart by anything they send. Those readers are counted under Filtered Traffic as Data centre address rather than lost, so you can see how many there are, and Settings, Tracking, Accuracy and privacy has the switch if the number is bigger than the machines it keeps out.<\/p><\/dd>\n<dt id=\"how%20do%20you%20tell%20an%20ai%20visitor%20from%20an%20ai%20crawler%3F\"><h3>How do you tell an AI visitor from an AI crawler?<\/h3><\/dt>\n<dd><p>They arrive by completely different routes and are handled at different points. A crawler identifies itself in the user agent, and GPTBot, ClaudeBot, CCBot, PerplexityBot, Bytespider, Applebot and Amazonbot are all refused by the collector before a row is written, so they never become traffic. A visitor is a browser carrying a referrer from an assistant's own domain. Only the second one reaches the AI screens, which is why every figure there is a person who read an answer and followed the citation.<\/p><\/dd>\n<dt id=\"an%20assistant%20is%20missing.%20can%20i%20add%20it%3F\"><h3>An assistant is missing. Can I add it?<\/h3><\/dt>\n<dd><p>Yes, with the <code>cnwa_ai_sources<\/code> filter. Each entry is a pattern matched against the referrer host and the name to show. The list is checked in order, so a narrow host goes above a broad one.<\/p><\/dd>\n<dt id=\"is%20the%20ai%20insights%20screen%20actually%20ai%3F\"><h3>Is the AI insights screen actually AI?<\/h3><\/dt>\n<dd><p>It is statistics, and the screen says so at the bottom of every run. Least squares fits for direction, median absolute deviation for outliers, share and concentration comparisons against the previous period of equal length. It runs on your server against your own rows, no model is called and nothing leaves the site. The name reflects what the screen is for, reading your numbers the way an analyst would, not a claim about how it works.<\/p><\/dd>\n<dt id=\"will%20generating%20insights%20slow%20my%20site%20down%3F\"><h3>Will generating insights slow my site down?<\/h3><\/dt>\n<dd><p>It cannot touch your front end, because it only runs from the dashboard when somebody presses the button. The run itself reads the selected period and the one before it across several dimensions, which on a large site takes a few seconds, and the result is cached for five minutes so a second press costs nothing. Nothing runs on opening the screen.<\/p><\/dd>\n<dt id=\"can%20i%20count%20returning%20visitors%3F\"><h3>Can I count returning visitors?<\/h3><\/dt>\n<dd><p>Yes, but it is off by default. Settings \u2192 Tracking \u2192 Visitor recognition widens the salt window from one day to 30, 90 or 365 days, or to never, where the same person is recognised for as long as the raw rows are kept. Until you change it the salt rotates daily and a reader returning tomorrow is a new visitor, which is the honest default for a plugin that stores nothing on the reader's device. Widening it changes what you are doing with visitor data, so update your privacy disclosures before you do.<\/p><\/dd>\n<dt id=\"the%20weekly%20email%20is%20set%20up%20but%20never%20arrives.\"><h3>The weekly email is set up but never arrives.<\/h3><\/dt>\n<dd><p>Settings, Weekly summary email, says when the last one went and, when one failed, the reason your mail system gave. Two things used to lose weeks and both are fixed in 1.5.0: a late scheduled run on the week boundary skipped the week, and a mail server that timed out mid send left the week marked as sent. If Send one now works and the weekly one still does not, scheduled tasks are not running on your site; Tracking health says so and why.<\/p><\/dd>\n<dt id=\"what%20does%20the%20tracking%20health%20check%20do%3F\"><h3>What does the tracking health check do?<\/h3><\/dt>\n<dd><p>It checks, every hour, that pageviews are arriving at the rate your site usually gets them, that yesterday was summarised and that scheduled maintenance runs. Once a day it also loads your home page to find the tracker and posts a signed test beacon to the collector, which is answered and never recorded. Results are in Settings, in Tools, Site Health, and in one notice on the Stats screen while something is wrong.<\/p><\/dd>\n<dt id=\"can%20i%20get%20alerts%20in%20slack%3F\"><h3>Can I get alerts in Slack?<\/h3><\/dt>\n<dd><p>Yes. Settings, Alerts takes an https webhook address as well as email addresses. Each alert is posted as JSON with a <code>text<\/code> field that Slack, Discord and most chat tools display as is.<\/p><\/dd>\n<dt id=\"why%20do%20iphone%20readers%20matter%20to%20the%20data%20centre%20rule%3F\"><h3>Why do iPhone readers matter to the data centre rule?<\/h3><\/dt>\n<dd><p>iCloud Private Relay sends Safari traffic out through Akamai, Cloudflare and Fastly, and part of that sits in ranges that are hosting networks. Apple publishes its relay exits so sites can tell them apart, and the plugin downloads that list weekly so relay readers are counted as the people they are.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20caching%20plugins%3F\"><h3>Does it work with caching plugins?<\/h3><\/dt>\n<dd><p>Yes. The beacon is client side and posts to a REST endpoint, so a cached HTML page still reports its own view.<\/p><\/dd>\n<dt id=\"why%20do%20my%20visitor%20counts%20differ%20from%20other%20analytics%20tools%3F\"><h3>Why do my visitor counts differ from other analytics tools?<\/h3><\/dt>\n<dd><p>ConvertNow filters bots aggressively and counts a visitor as unique within a single day. Tools that use persistent cookies count differently. Neither number is wrong, they measure different things.<\/p><\/dd>\n<dt id=\"where%20is%20my%20data%20stored%3F\"><h3>Where is my data stored?<\/h3><\/dt>\n<dd><p>In your own WordPress database, in eleven tables prefixed <code>cnwa_<\/code>, plus site icons and the Private Relay list in <code>wp-content\/uploads\/convertnow-analytics\/<\/code>, where plugin updates cannot touch them. Deleting the plugin keeps everything by default; switch on full removal in Settings and deleting it removes all of it. The country lookup table is a read only file inside the plugin folder and holds nothing about your visitors.<\/p>\n\n<p><strong>Clicks, downloads and searches<\/strong><\/p>\n\n<p>On from the moment you install, because a Clicks screen that stays empty until you find a switch is a setup step, and the plugin promises none. A click is one row per interaction rather than per page, so events keep their own retention window, ninety days by default, which keeps the table bounded. Each type can be switched off on its own:<\/p>\n\n<ul>\n<li>Outbound links, with the destination and the domain<\/li>\n<li>File downloads, matched by extension<\/li>\n<li>Email and phone links<\/li>\n<li>Anything you name with a CSS selector, labelled by <code>data-cnwa-label<\/code> or its own text<\/li>\n<li>Page views that returned a 404, and the pages that linked to them<\/li>\n<li>What people typed into your search box<\/li>\n<\/ul>\n\n<p>The last two are effectively free: they ride along with a page view that is already being recorded, so they cost one row and no extra request. Broken links and empty searches are usually the fastest wins on any site. The settings screen shows what event capture costs on your site, from your own traffic over the last thirty days.<\/p>\n\n<p><strong>Notes on the chart<\/strong><\/p>\n\n<p>A spike six weeks ago is just a shape unless something tells you what it was. Mark the day you redesigned, sent the newsletter or changed your prices, and the note stays on the chart with the traffic it explains. They are stored in one option row, so there is no table and nothing left behind.<\/p>\n\n<p><strong>A weekly email<\/strong><\/p>\n\n<p>Last week against the week before, your five most read pages and five biggest sources, in an email you can read in fifteen seconds. It comes from your own site, contains no tracking pixel, and reads from the daily summaries, so it costs the same whether you had a hundred visitors or a million. Off until you ask for it, and there is a button to send one now and find out whether email leaves your site at all.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.5.0<\/h4>\n\n<p>The trust release: the plugin now tells you when it is not counting, and counts more exactly when it is.<\/p>\n\n<p>Tracking health. Every hour the plugin checks that pageviews are arriving at your site's usual rate, that yesterday was summarised and that scheduled maintenance runs; once a day it loads your home page to find the tracker and posts a signed test beacon, never recorded, to both collector addresses. Results are in Settings, in Tools, Site Health, and in one notice on the Stats screen while something is actually wrong. It runs once a minute after activation.<\/p>\n\n<p>Alerts, by email and optionally to a webhook: tracking stopped, traffic well below or above the same hours on recent same weekdays, a wave of bots, AI assistant traffic moving sharply, and a surge of missing pages. Each is judged against the site's own numbers, stays quiet below a minimum volume and fires at most once a day. Off until switched on.<\/p>\n\n<p>Site icons. Referrers, campaign sources, assistants and crawler operators show the site's own icon instead of a coloured letter, so Google, GitHub, ChatGPT and Claude no longer look alike. Icons for the major sites are fetched in the background on install, a site seen for the first time gets its icon within the hour, and everything is stored under uploads so an update never removes it.<\/p>\n\n<p>The weekly email could skip weeks. A site whose scheduled run missed the send day rolled into the next calendar week and waited a week more; a mail server that timed out mid send left the week marked as sent. Both are fixed: a late run sends the week it missed, a failed send is retried, and the settings card shows when the next one goes and, after a failure, the reason the mail system gave. When WP-Cron has stopped altogether, the plugin now runs its own maintenance from visitor traffic where the server allows, and the health check says so.<\/p>\n\n<p>Settings survive updates by construction. Every upgrade snapshots the settings and restores anything a step did not explicitly mean to change, a copy of the settings is kept and restored if the row is ever lost, and updating by deactivating and reactivating now runs the upgrade instead of skipping it. The shared dashboard link is kept and shown on the Settings screen for as long as it is valid, rather than shown once and never again, and it keeps working if the site's security salts change.<\/p>\n\n<p>Accuracy. Signed in administrators and editors were being counted: the beacon posts without a login cookie, so the collector saw everybody as anonymous. They are excluded again. Safari readers using iCloud Private Relay were refused as data centre traffic; Apple's published exit list is now downloaded weekly and they are counted. Every pageview carries a small proof of work, a few milliseconds for a browser, so forged pageviews posted straight at the collector are refused; pages cached before this update are accepted for a week. Two pages opened in the same moment by one reader no longer start two visits.<\/p>\n\n<p>Speed. A pageview is answered before the rest of WordPress starts (the <code>cnwa_fast_collect<\/code> filter turns this off for code that adds <code>cnwa_should_count<\/code> late, from inside init), which cut the time a collect request takes to about a third in testing and more than doubled how many a server can take at once. Refused requests are appended and folded into their counters in batches, so a crawler wave no longer queues on shared rows. Rate limiting moved out of wp_options into a small table of its own, one query instead of four option writes per pageview. Summaries are built one transaction per day under a lock, and two redundant indexes were removed from the pageview table.<\/p>\n\n<h4>1.4.1<\/h4>\n\n<p>Charts that disagreed with the numbers above them now agree. The trend on every AI screen was drawing its summarised days and silently dropping the current one, so AI at a Glance showed a headline of twenty four views above a line that added up to twenty one, and the line fell to zero on today whatever the traffic was. The same gap left today out of the stacked charts on Authors, Landing Pages and Exit Pages. Both are fixed, and the trend, the ranked rows and the stacked chart on every screen are now checked against each other.<\/p>\n\n<p>Biggest assistant was measuring its share against the whole site rather than against assistant traffic, so a single assistant sending every AI visit on the site was reported as 0%. It now reads as a share of the views it is a share of.<\/p>\n\n<p>The weekly summary email no longer loses a week to a mail server having a bad hour. The week was being marked as sent before wp_mail was asked, and a refusal left it marked, so the email never went and nothing said why. A refused send is now retried on the following hourly runs, and the settings card says when the last one went out, or that the last one was refused and by what.<\/p>\n\n<p>Count a view after offers one second again, alongside two, three, five and ten.<\/p>\n\n<p>The four Maintenance buttons no longer shift sideways when one of them reports back: the answer goes in a fixed slot below the row, so the button you were about to press is still where you were looking. The same applies to the shared link and weekly email buttons.<\/p>\n\n<p>On the screens a filter cannot reach, because a refused request carries no country or device, the filter chips say so instead of sitting there looking active. Every trend panel is now drawn by one piece of code, which is how the revenue one had quietly lost its download control.<\/p>\n\n<h4>1.4.0<\/h4>\n\n<p>The human only release. Pageviews from data centre addresses are refused: two bundled tables of cloud and hosting ranges, about 45,000 of them, let the collector tell a headless browser on AWS from a reader at home even when everything else about the request looks right. It is on by default and switchable in Settings, and the refused requests are counted under Filtered Traffic as Data centre address. A pageview now waits two seconds for a reader who does nothing, up from one, and only trusted device events count as an interaction: a click dispatched by script proves nothing. A page that merely stayed on screen in a browser that also reports oddly is refused and filed as headless, and Headless Chrome is recognised from its client hints even under a rewritten agent string. Settings gains an interaction only rule for sites that want the strictest count there is.<\/p>\n\n<p>Filtered Traffic is rebuilt. Refused requests are now counted by hour and by page as well as by name, so the screens carry a dated chart with an axis, an hourly chart for today, a stack of what the traffic was over time, a chart of people against machines, the pages machines fetched most, and when in the day they come. AI Visibility gains Pages AI Reads, the pages the models are fetching, next to the pages assistants send people to. Proof of Presence shows every counted view by what proved a person was there, and the Overview carries a Not people card. The weekly email says how much was refused.<\/p>\n\n<p>Setup is now nothing: click, download, contact link, missing page and site search capture are on from install, and the settings screen shows only what matters, with the fine tuning folded away on its most accurate defaults. Saving one settings form can no longer reset a field on another, and the delivery settings (script mode, endpoint name, proxy header, allowed domains) finally have a save button of their own. The weekly email sends on the first cron run on or after its day rather than only on the day, so a quiet site no longer skips a week.<\/p>\n\n<p>Bugs fixed: on a site without pretty permalinks every dashboard request was a 404 and the screens sat on Loading; the upgrade routine logged a database error on every run under WP_DEBUG; the sidebar collapse button and the section dropdowns shared a style, which left two controls fighting each other and pushed the menu off the screen on tablets. The collapse button is gone and the sidebar is one menu with section dropdowns, becoming a single dropdown below 960 pixels. The engagement table scrolls sideways on phones instead of widening the page.<\/p>\n\n<h4>1.3.2<\/h4>\n\n<p>A pageview now has to prove somebody was there: the page has to render on screen, and then either somebody does something on it or it stays in front of them. The collect endpoint no longer accepts anonymous POSTs, and a route change only counts after an interaction. Expect lower numbers after this update, and nothing already recorded is rewritten.<\/p>\n\n<p>Everything refused is counted by name under Stats, so a low figure can be checked rather than taken on trust. AI crawlers and AI assistant fetches are kept apart from each other and from your visitors: a person who follows a citation out of ChatGPT is still a visitor, the machine that fetched the page on their behalf never was.<\/p>\n\n<p>The bot list was also making the opposite mistake. It matched company names rather than crawler names, so it refused the DuckDuckGo browser, Yandex's and Naver's phone apps, Petal Search, the Pinterest app and anyone reading a page inside Slack: seven of the fifty two real browsers now in the test set. There is one list instead of two overlapping ones, it has to be able to name a machine before it will refuse it, and Do Not Track exclusions are counted so a site honouring that signal can see where its traffic went.<\/p>\n\n<h4>1.3.0<\/h4>\n\n<p>Revenue reporting: WooCommerce and Easy Digital Downloads orders are read from the shop and attached to the visit that produced them, across seven new screens. Correct counting behind a page cache or a CDN, which was losing whole pages of traffic: the beacon now ships to everybody and exclusion happens on the collect request, page optimisers are told to leave it alone, and a shared CDN address no longer throttles the site.<\/p>\n\n<h4>1.2.3<\/h4>\n\n<p>Accuracy release, and your figures will get smaller. Four faults were inflating visitors and views: the no script image was counting machines, forwarded IP headers were believed on sites with no proxy, a retried beacon could store the same pageview twice, and bot filtering was an agent list and nothing else. All four are fixed and the stored history is cleaned in the background. Ranked reports also page and search properly now, so a site with thousands of pages can see past the top twenty.<\/p>\n\n<h4>1.2.2<\/h4>\n\n<p>An optional anonymous usage report, off on every install and never sent unless you agree. You are asked once, shown the exact payload from your own site first, and never asked again if you say no. Nothing about your visitors is included under any circumstances.<\/p>\n\n<h4>1.2.1<\/h4>\n\n<p>Data, then AI insights, runs a statistical read of the selected period on demand and returns ranked findings with the figure and the confidence behind each one. Everything is computed on your own server against your own rows. Also adds an optional longer visitor recognition window for counting returning readers, off by default.<\/p>\n\n<h4>1.2.0<\/h4>\n\n<p>Fewer requests, and a beacon that stops when your host says stop: it reads 429 and 503, honours Retry-After, holds a budget per visit, and a page read now costs one request instead of two with nothing lost. Counting is more complete too: readers whose browser ran no JavaScript are recorded, a blocked REST route no longer means lost traffic, and Do Not Track is no longer skipped by default on new installs.<\/p>\n\n<h4>1.1.1<\/h4>\n\n<p>Deleting the plugin keeps your settings, your shared link and your history by default, so updating by hand no longer wipes them. The AI screens now describe assistant traffic and nothing else, and authors, content types, assistants, cited pages and campaigns can all be filtered on.<\/p>\n\n<h4>1.1.0<\/h4>\n\n<p>AI Visibility: visits arriving from an assistant become their own channel with four screens of their own, and Gemini is no longer miscounted as organic search. Four optional statistical layers for the main chart, every one off until you switch it on. Goals and funnels read their own rules back as a sentence and funnel steps can be reordered.<\/p>\n\n<h4>1.0.0<\/h4>\n\n<p>First public release. Editorial reporting is the point of it: traffic by author, posts separated from pages and archives, and read depth per post. Real bounce rate and time on page from an engagement beacon rather than guessed from timestamps. Cookieless, self hosted, no account, no API key and no paid tier.<\/p>","raw_excerpt":"Human only analytics in your own database. AI crawlers, bots and headless browsers counted apart, never as visitors. Cookieless, no paywall.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/361489","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=361489"}],"author":[{"embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/prateekzare"}],"wp:attachment":[{"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=361489"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=361489"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=361489"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=361489"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=361489"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/es-ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=361489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}