Sticky Post from page 2 and on

Sticky posts are a pain in the butt, and is not properly implememnted IMHO. The source code clearly shows that sticky posts are only moved to the top on page one of the homepage (or any custom instance of WP_Query which emulates the homepage), sticky posts does not get removed from the paged pages. This, IMO, should be part of core, stickies should be moved to the top of page one and should be completely removed from the query after that.

As on the point of SEO, I’m not sure what the impact of this might be. SEO is a very advanced field that eludes me (and probably a lot of us on this stack). This might also be the wrong stack to ask such a question, I would think that the webmasters stack would be the best place to ask SEO related questions like this.

Anyways, to come back to the topic of stickies, what I always do is the following (Please follow the linked source code for better understanding):

  • Set ignore_sticky_posts to true on the home page via pre_get_posts. This will ignore the sticky posts function.

  • Still inside pre_get_posts, get the array of sticky posts and completely remove them from the query. This way, one do not have the headache of having to manually recalculate pagination due to offsets being used etc

  • Inject the sticky posts via the_posts filter into the main query’s loop.

What is nice about doing it this way is,

  • sticky posts are not part of the main query anymore

  • saves on having to recaculate pagination if you are going to remove sticky posts only from page 2. I have done this before, but is a bit messy

Lets look at the code: (NOTE The code is untested an requires PHP 5.4+)

add_action( 'pre_get_posts', function ( $q )
{
    if (    $q->is_home()       // Only target the homepage
         && $q->is_main_query() // Only target the main query
    ) {
        // Remove sticky posts
        $q->set( 'ignore_sticky_posts', 1 );

        // Get the sticky posts array
        $stickies = get_option( 'sticky_posts' );

        // Make sure we have stickies before continuing, else, bail
        if ( !$stickies )
            return;

        // Great, we have stickies, lets continue
        // Lets remove the stickies from the main query
        $q->set( 'post__not_in', $stickies );

        // Lets add the stickies to page one via the_posts filter
        if ( $q->is_paged() )
            return;

        add_filter( 'the_posts', function ( $posts, $q ) use ( $stickies )
        {
            // Make sure we only target the main query
            if ( !$q->is_main_query() )
                return $posts;

            // Get the sticky posts
            $args = [
                'posts_per_page' => count( $stickies ),
                'post__in'       => $stickies
            ]; 
            $sticky_posts = get_posts( $args );

            // Lets add the sticky posts in front of our normal posts
            $posts = array_merge( $sticky_posts, $posts );

            return $posts;
        }, 10, 2 );
    }
});