Starting with the 10.5 release, WooCommerce has updated how it selects the category to use in product permalinks when a product is assigned to multiple categories. The new behavior selects the deepest category (most ancestors in the hierarchy) rather than sorting by parent term ID.
Who Is Affected
This change only affects sites using one of these product permalink structures (found at Settings > Permalinks > Product permalinks):
- “Shop base with category” – URLs like
/shop/category-name/product-name/ - Custom base containing
%product_cat%– e.g.,/products/%product_cat%/
Sites using “Default”, “Shop base”, or custom structures without %product_cat% are not affected.
How to Check Your Permalink Structure
- Go to WordPress Admin > Settings > Permalinks
- Scroll to “Product permalinks”
- Check if “Shop base with category” is selected, or if your custom base contains
%product_cat%
What Changed
Before (WooCommerce < 10.5):
The category was selected by sorting all product categories by parent (descending) then term_id (ascending), and using the first result. This could produce inconsistent results depending on the order terms were created in the database.
$terms = wp_list_sort(
$terms,
array(
'parent' => 'DESC',
'term_id' => 'ASC',
)
);
$category_object = $terms[0];After (WooCommerce 10.5+):
The category is selected by finding the term with the most ancestors (deepest in the hierarchy). This produces more predictable and semantically correct permalinks.
// New logic iterates through terms and selects the one with most ancestors
$deepest_term = $terms[0];
foreach ( $terms as $term ) {
$ancestors = get_ancestors( $term->term_id, 'product_cat' );
if ( count( $ancestors ) > count( $deepest_ancestors ) ) {
$deepest_term = $term;
}
}Why This Change Was Made
The previous sorting logic could select a parent category over a more specific child category, resulting in less descriptive permalinks. For example, a product in both “Electronics” and “Electronics > Phones > Smartphones” might have used “Electronics” in its permalink instead of the more specific “Smartphones” path.
The new behavior ensures the most specific (deepest) category is used, producing permalinks like /shop/electronics/phones/smartphones/product-name/ instead of /shop/electronics/product-name/.
Impact on Existing Sites
URL Changes
Products assigned to multiple categories at different hierarchy depths may have different permalink URLs after this update.
SEO Considerations
WooCommerce’s built-in canonical redirect functionality (wc_product_canonical_redirect()) will automatically 301 redirect old URLs to the new canonical URL. This means:
- Bookmarked links will continue to work
- Search engine rankings should be preserved through proper redirects
- No manual redirect setup is required
Filter Behavior Change
The wc_product_post_type_link_product_cat filter now receives the deepest category as its first parameter instead of the sort-by-parent-ID result. Extensions using this filter will receive a different category object.
Restoring Previous Behavior
If your site or extension depends on the previous category selection logic, you can restore it using the wc_product_post_type_link_product_cat filter:
/**
* Restore legacy product permalink category selection behavior.
*
* Selects category by sorting on parent (DESC) then term_id (ASC),
* matching WooCommerce 9.5 and earlier behavior.
*
* @param WP_Term $category The deepest category (new default behavior).
* @param WP_Term[] $terms All categories assigned to the product.
* @param WP_Post $post The product post object.
* @return WP_Term The category to use in the permalink.
*/
function my_restore_legacy_permalink_category( $category, $terms, $post ) {
$sorted_terms = wp_list_sort(
$terms,
array(
'parent' => 'DESC',
'term_id' => 'ASC',
)
);
return $sorted_terms[0];
}
add_filter( 'wc_product_post_type_link_product_cat', 'my_restore_legacy_permalink_category', 10, 3 );Related Resources
If you have questions about this change, please open a discussion on the WooCommerce GitHub repository.
Leave a Reply