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
The bug ticket: https://core.trac.wordpress.org/ticket/51912
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; }