I'm building a view counter for blog posts in WordPress, and I want each real user to increment the post view count only once, not every time they refresh the page.
I tried to handle this with cookies, but it still feels off and I’m not sure if it’s the best approach. Here's what I have so far
function get_post_view_count($postID)
{
$key = 'post_views_count';
$count = get_post_meta($postID, $key, true);
return ($count === '') ? '0' : $count;
}
function track_post_views()
{
if (is_single()) {
global $post;
set_post_view_count($post->ID);
}
}
add_action('wp_head', 'track_post_views');
add_action('wp_head', 'track_post_views');
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
function set_post_view_count($postID)
{
$key = 'post_views_count';
// Check if cookie exists for this post
$cookie_name = 'viewed_post_' . $postID;
if (!isset($_COOKIE[$cookie_name])) {
$count = get_post_meta($postID, $key, true);
if ($count === '') {
$count = 0;
delete_post_meta($postID, $key);
add_post_meta($postID, $key, 1);
} else {
$count++;
update_post_meta($postID, $key, $count);
}
// Set cookie for 2 hours
setcookie($cookie_name, '1', time() + 2 * 3600, '/'); // expires in 2 hours
}}
in single.php
<?php echo '👁️ ' . get_post_view_count(get_the_ID()) . ' views'; ?>
This works, but currently refreshing the page still increments the count, or at least that's what I’m seeing sometimes.
How can I make sure the count only increases once per real user (or browser session) per post, and not on every refresh?
Would appreciate any tips on improving this logic or suggestions for a better approach!
WordPress sites are typically served through multiple layers of caching (server, plugin, or CDN), so relying on PHP cookies or wp_head
hooks can be unreliable for view tracking.
A better approach is to move this logic to the frontend: Use JavaScript to make an AJAX call to a small PHP endpoint that increments the post's view count in the database. Before firing the AJAX call, check localStorage
for a list of posts the user has already viewed (you can store post IDs or slugs). This method is cache-proof and works across reloads without counting multiple views from the same user/browser.
It's simple, effective, and doesn't rely on fingerprinting or cookies — which may be blocked or delayed by browsers or caching layers.