Make WordPress Core

Opened 3 weeks ago

Closed 3 weeks ago

#65941 closed defect (bug) (duplicate)

WordPress 7.1: Valid XML sitemaps return HTTP 404 when no standard posts are published

Reported by: andreasca Owned by:
Priority: normal Milestone:
Component: Sitemaps Version: 7.1
Severity: normal Keywords:
Cc: Focuses:

Description

After upgrading to WordPress 7.1, valid native WordPress XML sitemaps can return HTTP 404 despite generating and displaying valid XML content.

The issue occurs when a website has no published standard posts but does have other public content, such as pages or WooCommerce products.

The sitemap index and its individual child sitemaps are generated correctly, but the HTTP response status is incorrectly set to 404.

Steps to reproduce

Install WordPress 7.1.

Delete or unpublish all standard blog posts.

Ensure the site has published pages or other public content types, such as WooCommerce products.

Enable the native WordPress sitemap functionality.

Request the sitemap index:

curl -i https://example.com/wp-sitemap.xml

Observe that the response contains valid XML but returns HTTP 404.

The same issue can affect individual sitemap URLs:

/wp-sitemap-posts-page-1.xml
/wp-sitemap-posts-product-1.xml
/wp-sitemap-taxonomies-product_brand-1.xml
/wp-sitemap-taxonomies-product_cat-1.xml

On a WooCommerce site with published products, requesting the following URL returned HTTP 200:

/wp-sitemap.xml?post_type=product

The XML response body was identical to the ordinary sitemap request, demonstrating that the XML generation itself works correctly and that the problem concerns HTTP status handling.

Expected behavior

Valid sitemap indexes and child sitemaps should return:

HTTP/2 200
Content-Type: application/xml; charset=UTF-8

This should work regardless of whether any standard WordPress blog posts are published.

Actual behavior

The sitemap index and valid child sitemaps return:

HTTP/2 404
Content-Type: application/xml; charset=UTF-8

The response nevertheless contains valid sitemap XML.

Suspected cause

WordPress 7.1 introduced the is_sitemap() conditional and the corresponding $is_sitemap property in WP_Query.

Related ticket:

https://core.trac.wordpress.org/ticket/51543

Related changeset:

https://core.trac.wordpress.org/changeset/62664/

The changeset explicitly states that sitemap requests are no longer treated as the home page.

Previously, sitemap requests could satisfy is_home(), which meant that WP::handle_404() would avoid setting a 404 response even when the main query returned no standard posts.

In WordPress 7.1, sitemap requests are excluded from is_home(), but WP::handle_404() does not include a corresponding exemption for is_sitemap():

if ( is_admin() || is_robots() || is_favicon() ) {
    $set_404 = false;
}

Later in the method, the existing exemptions include:

is_home() || is_search() || is_feed()

However, is_sitemap() is not included.

When the main query contains no standard posts, WordPress therefore sets HTTP 404 before the sitemap renderer outputs otherwise valid XML.

Relevant source:

https://developer.wordpress.org/reference/classes/wp/handle_404/

Suggested fix

Consider exempting valid sitemap requests from the default 404 handling, similarly to robots.txt and favicon requests:

if ( is_admin() || is_robots() || is_favicon() || is_sitemap() ) {
    $set_404 = false;
}

Alternatively, ensure that the native sitemap renderer explicitly sets HTTP 200 when rendering a valid sitemap index or provider response.

Any fix should preserve HTTP 404 for genuinely invalid sitemap providers, invalid sitemap pages, and disabled sitemap functionality.

Temporary workaround

The issue can be mitigated through the pre_handle_404 filter:

<?php
add_filter( 'pre_handle_404', function ( $preempt, $query ) {

    if ( $preempt ) {
        return $preempt;
    }

    $sitemap = get_query_var( 'sitemap' );

    if ( empty( $sitemap ) || ! function_exists( 'wp_sitemaps_get_server' ) ) {
        return $preempt;
    }

    $server = wp_sitemaps_get_server();

    if ( ! $server->sitemaps_enabled() ) {
        return $preempt;
    }

    if (
        'index' !== $sitemap
        && ! $server->registry->get_provider( $sitemap )
    ) {
        return $preempt;
    }

    status_header( 200 );

    return true;

}, 10, 2 );

After applying this workaround, the sitemap index and all affected child sitemaps returned HTTP 200.

Impact

Search engines and AI crawlers may reject or ignore otherwise valid sitemap files because the HTTP response indicates that the resource does not exist.

This particularly affects WordPress installations that use pages or custom post types without publishing traditional blog posts, including WooCommerce stores.

Change History (1)

#1 @ocean90
3 weeks ago

  • Milestone Awaiting Review
  • Resolutionduplicate
  • Status newclosed

Duplicate of #65936.

Note: See TracTickets for help on using tickets.