Commit e09664501b0 for woocommerce
commit e09664501b06671903b01bbb739c3da32ef80977
Author: Luigi Teschio <gigitux@gmail.com>
Date: Tue Nov 10 13:24:19 2026 +0100
Exclude non-indexable WooCommerce pages from core sitemaps (#70712)
* Exclude non-indexable WooCommerce pages from core sitemaps
* Add changelog entry for core sitemap exclusions
* Centralize WooCommerce search engine visibility handling
* Register wp_robots filter in wc-template-functions.php for better control over search engine visibility
diff --git a/plugins/woocommerce/changelog/fix-core-sitemap-private-pages b/plugins/woocommerce/changelog/fix-core-sitemap-private-pages
new file mode 100644
index 00000000000..5d1bbfe9b53
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-core-sitemap-private-pages
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Exclude Cart, Checkout, My Account, and Review Order pages from the WordPress sitemap.
diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php
index c946d959a3e..935fa2bb8f8 100644
--- a/plugins/woocommerce/includes/class-woocommerce.php
+++ b/plugins/woocommerce/includes/class-woocommerce.php
@@ -56,6 +56,7 @@ use Automattic\WooCommerce\Internal\Caches\TaxRateVersionStringInvalidator;
use Automattic\WooCommerce\Internal\CustomerEmailVerification\CustomerEmailVerification;
use Automattic\WooCommerce\StoreApi\Utilities\Ucp\IdempotencyStore;
use Automattic\WooCommerce\Internal\StructuredData\OpenGraph;
+use Automattic\WooCommerce\Internal\SearchEngineVisibility;
/**
* Main WooCommerce Class.
@@ -425,6 +426,7 @@ final class WooCommerce {
$container->get( SettingsScreen::class );
$container->get( AbilitiesTracking::class );
$container->get( OpenGraph::class );
+ $container->get( SearchEngineVisibility::class );
$container->get( MenuIcons::class );
/**
diff --git a/plugins/woocommerce/includes/wc-deprecated-functions.php b/plugins/woocommerce/includes/wc-deprecated-functions.php
index 627c73ca24b..a24fb48d8e0 100644
--- a/plugins/woocommerce/includes/wc-deprecated-functions.php
+++ b/plugins/woocommerce/includes/wc-deprecated-functions.php
@@ -1403,3 +1403,14 @@ function get_woocommerce_api_url( $path ) {
add_filter( 'pre_option_woocommerce_api_enabled', function() {
return WC()->legacy_rest_api_is_available() ? 'yes' : 'no';
} );
+
+/**
+ * Legacy noindex output for transactional pages.
+ *
+ * @since 3.2.0
+ * @deprecated 11.4.0 Use wc_page_no_robots() to filter robots directives instead.
+ * @return void
+ */
+function wc_page_noindex() {
+ wc_deprecated_function( __FUNCTION__, '11.4.0', 'wc_page_no_robots' );
+}
diff --git a/plugins/woocommerce/includes/wc-template-functions.php b/plugins/woocommerce/includes/wc-template-functions.php
index 9a380a4ea8a..7305e8e0663 100644
--- a/plugins/woocommerce/includes/wc-template-functions.php
+++ b/plugins/woocommerce/includes/wc-template-functions.php
@@ -14,6 +14,7 @@ use Automattic\WooCommerce\Enums\OrderStatus;
use Automattic\WooCommerce\Enums\PaymentGatewayFeature;
use Automattic\WooCommerce\Enums\ProductType;
use Automattic\WooCommerce\Admin\Features\Fulfillments\Fulfillment;
+use Automattic\WooCommerce\Internal\SearchEngineVisibility;
use Automattic\WooCommerce\Internal\Utilities\HtmlSanitizer;
use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -4519,25 +4520,6 @@ function wc_empty_cart_message() {
echo '<div class="woocommerce-notices-wrapper wc-empty-cart-message">' . $notice . '</div>';
}
-/**
- * Disable search engines indexing core, dynamic, cart/checkout pages.
- *
- * @todo Deprecated this function after dropping support for WP 5.6.
- * @since 3.2.0
- * @return void
- */
-function wc_page_noindex() {
- // wp_no_robots is deprecated since WP 5.7.
- if ( function_exists( 'wp_robots_no_robots' ) ) {
- return;
- }
-
- if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) {
- wp_no_robots();
- }
-}
-add_action( 'wp_head', 'wc_page_noindex' );
-
/**
* Disable search engines indexing core, dynamic, cart/checkout pages.
* Uses "wp_robots" filter introduced in WP 5.7.
@@ -4547,11 +4529,7 @@ add_action( 'wp_head', 'wc_page_noindex' );
* @return array Filtered robots directives.
*/
function wc_page_no_robots( $robots ) {
- if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) {
- return wp_robots_no_robots( $robots );
- }
-
- return $robots;
+ return wc_get_container()->get( SearchEngineVisibility::class )->handle_wp_robots( $robots );
}
add_filter( 'wp_robots', 'wc_page_no_robots' );
diff --git a/plugins/woocommerce/src/Internal/SearchEngineVisibility.php b/plugins/woocommerce/src/Internal/SearchEngineVisibility.php
new file mode 100644
index 00000000000..ec7279dd514
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/SearchEngineVisibility.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Search engine visibility for WooCommerce pages.
+ *
+ * @package WooCommerce
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Internal;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Controls robots directives and core sitemap exclusions for WooCommerce pages.
+ *
+ * @since 11.4.0
+ * @internal
+ */
+class SearchEngineVisibility {
+
+ /**
+ * Register the sitemap filter.
+ *
+ * @internal
+ */
+ final public function init(): void {
+ // Keep wp_robots registered in wc-template-functions.php through wc_page_no_robots() so extensions can remove it.
+ add_filter( 'wp_sitemaps_posts_query_args', array( $this, 'handle_wp_sitemaps_posts_query_args' ), 10, 2 );
+ }
+
+ /**
+ * Add noindex to the robots directives for transactional pages.
+ *
+ * @internal
+ *
+ * @param mixed $robots Robots directives from earlier filters.
+ * @return mixed Filtered robots directives.
+ */
+ public function handle_wp_robots( $robots ) {
+ return is_array( $robots ) && $this->is_noindex_page() ? wp_robots_no_robots( $robots ) : $robots;
+ }
+
+ /**
+ * Exclude pages that WooCommerce marks noindex or redirects on direct visits.
+ *
+ * @internal
+ *
+ * @param mixed $args Query arguments from earlier filters.
+ * @param string $post_type Sitemap post type.
+ * @return mixed Filtered query arguments.
+ */
+ public function handle_wp_sitemaps_posts_query_args( $args, $post_type ) {
+ if ( 'page' !== $post_type || ! is_array( $args ) ) {
+ return $args;
+ }
+
+ $excluded_ids = $this->get_excluded_page_ids( true );
+ if ( $excluded_ids ) {
+ $args['post__not_in'] = array_merge( (array) ( $args['post__not_in'] ?? array() ), $excluded_ids );
+ }
+
+ return $args;
+ }
+
+ /**
+ * Check whether the current page should receive a noindex directive.
+ *
+ * @return bool
+ */
+ private function is_noindex_page(): bool {
+ $page_ids = $this->get_excluded_page_ids();
+
+ return $page_ids && is_page( $page_ids );
+ }
+
+ /**
+ * Resolve transactional page IDs, optionally including the Review Order host page.
+ *
+ * @param bool $include_review_order Whether to include the redirecting host page for sitemap exclusions.
+ * @return int[]
+ */
+ private function get_excluded_page_ids( bool $include_review_order = false ): array {
+ $pages = array( 'cart', 'checkout', 'myaccount' );
+ if ( $include_review_order ) {
+ $pages[] = 'review_order';
+ }
+
+ // Prime caches to reduce future queries.
+ wp_prime_option_caches(
+ array_map(
+ static fn( string $page ): string => 'woocommerce_' . $page . '_page_id',
+ $pages
+ )
+ );
+
+ $excluded_ids = array();
+ foreach ( $pages as $page ) {
+ $page_id = wc_get_page_id( $page );
+ if ( $page_id > 0 ) {
+ $excluded_ids[] = $page_id;
+ }
+ }
+
+ return $excluded_ids;
+ }
+}