WordPress at a glance

WP Sitemap 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 executes and interferes with the normal operation of the sitemap.

The bug is tested on WP 5.6.1

Let's simulate the problem

To do this, create 5 posts of the page type, 1 post of the post type and limit the number of links on the sitemap page to 1 (using such code):

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

Now going to the sitemap page /wp-sitemap-posts-page-4.xml we will see 4-th pagination page of the sitemap, everything seems to be OK, but if we look at the response status, 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 is made first.

    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 query 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 query is not related to the sitemap and the current request parameters. This query will return an empty result, because we don't have so many posts.

Next, the WP::handle_404() method will work, which will set status_header( 404 ); because $wp_query->posts = array() - the query did not get any data.

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

Solution (in the form of a crutch)

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;
}
No comments
    Log In