Skip to content

Commit 8d898db

Browse files
committed
Sitemaps: Don't 404 valid sitemaps on sites with no posts.
`WP::handle_404()` sets a 404 when the main query matches no posts and no exception applies. Sitemap requests were never among those exceptions; they were shielded only incidentally, by falling through to `is_home`, and in r62664 that fallthrough was removed. A site with no published posts therefore served a complete, valid sitemap under a 404 status, which search engines discard. Exempt sitemap and stylesheet routes there, alongside the existing admin, robots and favicon exceptions. Since `handle_404()` no longer decides the status for these requests, every sitemap 404 now has to be issued by `WP_Sitemaps::render_sitemaps()` instead: an unregistered provider, an unrecognized stylesheet type, and a route whose query vars do not survive `sanitize_text_field()` would each otherwise be served as a 200 on an arbitrary URL. These share a `send_404()` helper, which also sends the no-cache headers `handle_404()` was previously contributing, so an intermediary does not retain a 404 for a route that becomes valid once the site has more content. Sitemaps disabled via the `wp_sitemaps_enabled` filter, and providers with an empty URL list, keep the status they already had; whether the latter should render an empty sitemap instead is #61293. Developed in WordPress/wordpress-develop#13247. Follow-up to r48072, r48523, r62664. Reviewed by adamsilverstein. Merges r63570 to the 7.1 branch. Merged to the 7.0 branch in error in r63573 and reverted in r63574. Props iamchitti, westonruter, fernandot, wildworks, harishtewari, l1onofjudah, luksusspokoju, abrahamfariaz, andreasca, siliconforks, adamsilverstein, audrasjb, ocean90, mrkenobi. See #39157, #61293. Fixes #65945. Built from https://develop.svn.wordpress.org/branches/7.1@63575 git-svn-id: http://core.svn.wordpress.org/branches/7.1@62751 1a063a9b-81f0-0310-95a4-ce76da25c4cd
1 parent a62fc89 commit 8d898db

3 files changed

Lines changed: 48 additions & 11 deletions

File tree

wp-includes/class-wp.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,9 @@ public function handle_404() {
746746

747747
$set_404 = true;
748748

749-
// Never 404 for the admin, robots, or favicon.
750-
if ( is_admin() || is_robots() || is_favicon() ) {
749+
// Never 404 here for the admin, robots, favicon, or sitemaps.
750+
// Sitemap routes send their own status in WP_Sitemaps::render_sitemaps().
751+
if ( is_admin() || is_robots() || is_favicon() || is_sitemap() || get_query_var( 'sitemap-stylesheet' ) ) {
751752
$set_404 = false;
752753

753754
// If posts were found, check for paged content.

wp-includes/sitemaps/class-wp-sitemaps.php

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,30 +157,45 @@ public function register_rewrites() {
157157
* Renders sitemap templates based on rewrite rules.
158158
*
159159
* @since 5.5.0
160-
*
161-
* @global WP_Query $wp_query WordPress Query object.
162160
*/
163161
public function render_sitemaps() {
164-
global $wp_query;
162+
/*
163+
* Bail early if this isn't a sitemap or stylesheet route.
164+
*
165+
* This runs on every front-end request, so it comes before any
166+
* sanitizing. The raw query vars are tested here, matching
167+
* WP::handle_404(), which exempts sitemap requests from its own 404 on
168+
* the same basis. Testing the sanitized values instead would let a
169+
* request that handle_404() exempted fall through both, leaving it a 200.
170+
*/
171+
if ( ! get_query_var( 'sitemap' ) && ! get_query_var( 'sitemap-stylesheet' ) ) {
172+
return;
173+
}
165174

166175
$sitemap = sanitize_text_field( get_query_var( 'sitemap' ) );
167176
$object_subtype = sanitize_text_field( get_query_var( 'sitemap-subtype' ) );
168177
$stylesheet_type = sanitize_text_field( get_query_var( 'sitemap-stylesheet' ) );
169178
$paged = absint( get_query_var( 'paged' ) );
170179

171-
// Bail early if this isn't a sitemap or stylesheet route.
180+
// Force a 404 and bail early if the route did not survive sanitizing.
172181
if ( ! ( $sitemap || $stylesheet_type ) ) {
182+
$this->send_404();
173183
return;
174184
}
175185

176186
if ( ! $this->sitemaps_enabled() ) {
177-
$wp_query->set_404();
178-
status_header( 404 );
187+
$this->send_404();
179188
return;
180189
}
181190

182191
// Render stylesheet if this is stylesheet route.
183192
if ( $stylesheet_type ) {
193+
// Force a 404 and bail early if the stylesheet type is not recognized.
194+
if ( ! in_array( $stylesheet_type, array( 'sitemap', 'index' ), true ) ) {
195+
$this->send_404();
196+
return;
197+
}
198+
184199
$stylesheet = new WP_Sitemaps_Stylesheet();
185200

186201
$stylesheet->render_stylesheet( $stylesheet_type );
@@ -197,7 +212,9 @@ public function render_sitemaps() {
197212

198213
$provider = $this->registry->get_provider( $sitemap );
199214

215+
// Force a 404 and bail early if the requested provider is not registered.
200216
if ( ! $provider ) {
217+
$this->send_404();
201218
return;
202219
}
203220

@@ -209,15 +226,34 @@ public function render_sitemaps() {
209226

210227
// Force a 404 and bail early if no URLs are present.
211228
if ( empty( $url_list ) ) {
212-
$wp_query->set_404();
213-
status_header( 404 );
229+
$this->send_404();
214230
return;
215231
}
216232

217233
$this->renderer->render_sitemap( $url_list );
218234
exit;
219235
}
220236

237+
/**
238+
* Sends a 404 for a sitemap route that cannot be served.
239+
*
240+
* WP::handle_404() exempts sitemap requests, so every sitemap 404 is issued
241+
* here instead. That includes the no-cache headers handle_404() sends with
242+
* its own 404, so an intermediary does not retain a 404 for a route that
243+
* becomes valid once the site has more content.
244+
*
245+
* @since 7.1.1
246+
*
247+
* @global WP_Query $wp_query WordPress Query object.
248+
*/
249+
private function send_404(): void {
250+
global $wp_query;
251+
252+
$wp_query->set_404();
253+
status_header( 404 );
254+
nocache_headers();
255+
}
256+
221257
/**
222258
* Redirects a URL to the wp-sitemap.xml
223259
*

wp-includes/version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @global string $wp_version
1818
*/
19-
$wp_version = '7.1.1-alpha-63572';
19+
$wp_version = '7.1.1-alpha-63575';
2020

2121
/**
2222
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.

0 commit comments

Comments
 (0)