Bug: 404 response status for pagination

The essence of the problem is that the main WP request (which is not needed in this case) is still being made and interferes with the normal operation of the sitemap.

The bug is tested on WP 5.6.1

Modeling the problem

To do this, create 5 posts with page posts type, 1 post with post post type and limit the max number of links up to 1 on sitemap page, use this code:

add_filter( 'wp_sitemaps_max_urls', function(){
	return 1;
} );

Now if we visit sitemap page /wp-sitemap-posts-page-4.xml we will see 4 sitemap pagination pages. Everything seems to be OK, but if we look at the response status code, we will see 404 (this status is not suitable for search engines).

Why is this happening?

When we visit the /wp-sitemap-posts-page-4.xml page, the main WP request occurs WP::main():

public function main( $query_args = '' ) {
	$this->init();

	$this->parse_request( $query_args );
	$this->send_headers();
	$this->query_posts();  //> — makes query
	$this->handle_404();   //> — checks whether there is data, if not, sets the status 404
	$this->register_globals();

	do_action_ref_array( 'wp', array( &$this ) );
}

In this case, the main request looks like this:

SELECT SQL_CALC_FOUND_ROWS  wp_posts.* FROM wp_posts
	WHERE 1=1  AND wp_posts.post_type = 'post'
		AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
	ORDER BY wp_posts.post_date DESC
	LIMIT 30, 10

As we can see, this request is not related to the sitemap and the current request parameters at all. This request will return an empty result, because we don't have so many posts of post post type.

Next, the method WP::handle_404() is triggered. It sets status_header( 404 ) because of $wp_query->posts = array() - the request did not receive any data.

Next, the template_redirect hook is triggered, on which runs all sitemap logic. But 404 status is already set!

The Temporary solution (before the bug will be solved in WP core)

Variant 3:

// Fix for sitemaps pagination bug. Additionally improve performance a little.
// @see https://core.trac.wordpress.org/ticket/51912
// TODO: check necessity and maybe delete
add_action( 'parse_request', 'fix_sitemaps_pagination_wp_bug' );
function fix_sitemaps_pagination_wp_bug( $wp ){

	if( empty( $wp->query_vars['sitemap'] ) && empty( $wp->query_vars['sitemap-stylesheet'] ) )
		return;

	$GLOBALS['wp_query']->query_vars = $wp->query_vars;

	wp_sitemaps_get_server()->render_sitemaps();
}

Variant 2:

// Fix sitemaps pagination bug. Additionally improve performance a little.
// @see https://core.trac.wordpress.org/ticket/51912
// TODO: check necessity and maybe delete
add_action( 'parse_query', 'fix_sitemaps_pagination_wp_bug' );
function fix_sitemaps_pagination_wp_bug( $wp_query ){

	// change render_sitemaps() call to run it before main query but after query vars are set.
	if(
		isset( $wp_query->query['sitemap'] ) &&
		$wp_query->is_main_query() &&
		remove_action( 'template_redirect', [ wp_sitemaps_get_server(), 'render_sitemaps' ] )
	){
		wp_sitemaps_get_server()->render_sitemaps();
	}

}

Variant 1:

// Fix for sitemaps pagination bug. Additionally improve performance a little.
// @see https://core.trac.wordpress.org/ticket/51912
// TODO: check necessity and maybe delete
add_filter( 'posts_request', 'fix_sitemaps_pagination_wp_bug', 10, 2 );
function fix_sitemaps_pagination_wp_bug( $request, $wp_query ){
	global $wpdb;

	if( isset( $wp_query->query['sitemap'] ) && $wp_query->is_main_query() ){
		return "SELECT * FROM $wpdb->posts LIMIT 1";
	}

	return $request;
}