How can I display 7 posts on the home page, but 9 posts on the subsequent pages?

The home page of my site is set to show the 9 latest posts (via the setting in the admin area), and then at the bottom I have the standard pagination links which allow visitors to view more posts.

I would like to have the home page (and only the home page) show 7 latest posts, but then on the subsequent pages – 2, 3, 4 etc have 9 posts showing. I also want the categories and archives to display 9 posts too.

All the methods I have tried so far either result in missing and duplicated posts, or I end up with an extra page showing in the pagination which results in a 404.

So far I’ve tried variations of the answer posted here:

Pagination returns 404 after page 20

And also here (Case #2: Conditional Offset):

Different 'posts_per_page' setting for first, and rest of the paginated pages?

Neither seems to work for what I want to achieve, but I feel like I’m close to a solution and could be missing something obvious.

Here’s the most recent version of the code I tried (added to functions.php), which resulted in too many pagination links showing (the last link caused a 404):

function home_paged_offset( $query ) {
$ppp = get_option( 'posts_per_page' );
$first_page_ppp = 7;
$paged = $query->query_vars[ 'paged' ];
if( $query->is_home() && $query->is_main_query() ) {
    if( !is_paged() ) {
        $query->set( 'posts_per_page', $first_page_ppp );
    } else {
        $paged_offset = $first_page_ppp + ( ($paged - 2) * $ppp );
        $query->set( 'offset', $paged_offset );
    }
}
}
add_action( 'pre_get_posts', 'home_paged_offset' );

function home_adjust_paged_offset_pagination( $found_posts, $query ) {
$ppp = get_option( 'posts_per_page' );
$first_page_ppp = 7;
$paged = $query->query_vars[ 'paged' ];
if( $query->is_home() && $query->is_main_query() ) {
    if( !is_paged() ) {
        return( $found_posts );
    } else {
        return( $found_posts - ($first_page_ppp - $ppp) );
    }
}
return $found_posts;
}
add_filter( 'found_posts', 'home_adjust_paged_offset_pagination', 10, 2 );

Solutions Collecting From Web of "How can I display 7 posts on the home page, but 9 posts on the subsequent pages?"

Ok this was really tricky. I have so far managed to work around it by lying to the WordPress global $wp_query. Here is how.

In your theme’s functions.php you can add these functions to show 7 posts on first page, and 9 posts on any other page.

Okay, that code works so far but you’ll notice that the pagination is incorrect on first page. Why? This is because WordPress uses the posts per page on first page (which is 7) and get the number of pages by doing division like ( 1000 / 7 ) = total posts. But what we need is to make the pagination take into account that on next pages we’ll show 9 posts per page, not 7. With filters, you cannot do this but if you add this hack to your template just before the_posts_pagination() function it’ll work as you expect.

The trick is to change the max_num_pages inside $wp_query global variable to our custom value and ignore WP calculation during the pagination links display only for first page.

global $wp_query;

// Needed for first page only
if ( ! $wp_query->is_paged ) {
    $all_posts_except_fp = ( $wp_query->found_posts - 7 ); // Get us the found posts except those on first page
    $wp_query->max_num_pages = ceil( $all_posts_except_fp / 9 ) + 1; // + 1 the first page we have containing 7 posts
}

And this is the code to put in functions.php to filter the query.

add_action('pre_get_posts', 'myprefix_query_offset', 1 );
function myprefix_query_offset(&$query) {

    if ( ! $query->is_home() ) {
        return;
    }

    $fp = 7;
    $ppp = 9;

    if ( $query->is_paged ) {
        $offset = $fp + ( ($query->query_vars['paged'] - 2) * $ppp );
        $query->set('offset', $offset );
        $query->set('posts_per_page', $ppp );

    } else {
        $query->set('posts_per_page', $fp );
    }

}

add_filter('found_posts', 'myprefix_adjust_offset_pagination', 1, 2 );
function myprefix_adjust_offset_pagination($found_posts, $query) {

    $fp = 7;
    $ppp = 9;

    if ( $query->is_home() ) {
        if ( $query->is_paged ) {
            return ( $found_posts + ( $ppp - $fp ) );
        }
    }
    return $found_posts;
}

function pagination($query_string){
global $posts_per_page, $paged;
$my_query = new WP_Query($query_string .”&posts_per_page=-1″);
$total_posts = $my_query->post_count;
if(empty($paged))$paged = 1;
$prev = $paged – 1;
$next = $paged + 1;
$range = 5;
$showitems = ($range * 2)+1;
$pages = ceil($total_posts/$posts_per_page);
if(1 != $pages){
echo ”;
echo ($paged > 1 && $showitems < $pages)? ‘«‘:””;
for ($i=1; $i <= $pages; $i++){
if (1 != $pages &&( !($i >= $paged+$range-0 || $i <= $paged-$range-1) || $pages <= $showitems )){
echo ($paged == $i)? ”.$i.’‘:”.$i.’‘;}}
echo ($paged < $pages && $showitems < $pages) ? ‘»‘ :””;echo “\n”;}}