I am trying to add a read posts function to my Wordpress website. To achieve this, I create a cookie on which I store all the IDs of read posts. Every time a post is loaded, the following function is fired :

function readPosts() {
    if (is_single()){
        $postId = get_the_id() . "/";
        $cookie = empty($_COOKIE['readposts']) ? "/0/" : $_COOKIE['readposts'];
        if(strpos($cookie, ("/" . $postId)) === false){
            $val = $cookie . $postId;
            setcookie('readposts', $val, time() + 3141592, "/");
add_action('get_header', 'readposts');

I tried using different hooks, such as wp , init or template_redirect but all are giving the same result. Instead of appending only the current post's ID to the cookie, it adds two IDs. The current post's ID and the ID of the post that was published just after the current post. Like if I was loading two different posts. But it only adds the ID of a more recent post if it's not already in the cookie and of course if there is a more recent post!

Here's how the cookie looks like before loading a page : /0/221/46/ And after : /0/221/46/292/82/

I have no idea why this is happening. It looks like dark magic for me. If any of you have an idea, I'd be very grateful for your help! Thanks

2 Answers 2


The core of the problem is that you set cookies when the content is being accessed, not when it is read. Browsers can try to prefetch content, and your description sound like the browser is prefecthing the "next" link found in the header.

From experience, you should avoid setting cookies on the server and prefer setting them on client side (add to the html a small JS that will set the cookie) or even better if possible, use local storage instead.


I went the JavaScript way as @Mark Kaplun suggested and it works. There is no more duplicate. Here's my JS for those interested:

jQuery(document).ready(function($) {
    var name = "readposts=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');

    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        if (c.indexOf(name) == 0) {
            var cookie = c.substring(name.length, c.length);

    if(cookie == "")
        cookie = "/0/" + my_script_vars.postID + "/";
    else if(cookie.indexOf("/" + my_script_vars.postID + "/") === -1)
        cookie += my_script_vars.postID + "/";

    var d = new Date();
    d.setTime(d.getTime() + (30*24*60*60*1000));
    var expires = "expires="+ d.toUTCString();
    document.cookie = "readposts=" + cookie + ";" + expires + ";path=/";

And here's how I tell WP to load it, and make postID accessible from JS :

function wpse268423_enqueue_cookie_script(){    
     if (is_single()){
          global $post;
          wp_enqueue_script('cookie_read_posts', get_template_directory_uri() 
                    . '/js/cookie_read_posts.js' );
          wp_localize_script('cookie_read_posts', 'my_script_vars', array(
                    'postID' => $post->ID)
add_action('get_header', 'wpse268423_enqueue_cookie_script');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.