WooCommerce | Same Product Base & Shop Base

Here is a simple* code, modified after the solution by Le Van Toan outlined on his blog based in Vietnam. Have you ever tried to setup your own custom permalinks in WooCommerce so that the product base and the shop base are one and the same? This plugin makes it so.

Desired WooCommerce Shop/Products base structure:

shop page – website.com/shop/
product parent category archive page – website.com/shop/parent-category-slug/
product child category archive page – website.com/shop/parent-category-slug/child-category-slug/
single product page – website.com/shop/parent-category-slug/child-category-slug/single-product-page-slug

Here is what you need to set in Settings > Permalinks
visit website.com/wp-admin/options-permalink.php

Optional > Product category base: shop
Product permalinks > Custom base: /shop/%product_cat%/

Here is the code, for your functions.php

function category_base_is_shop_base(){
// Get all product categories
$terms = get_terms(array(
'taxonomy' => 'product_cat',
'post_type' => 'product',
'hide_empty' => false,

// Check if terms are retrieved successfully
if ($terms && !is_wp_error($terms)) {
$siteurl = esc_url(home_url('/'));
// Loop through each term to set up custom rewrite rules
foreach ($terms as $term) {
$term_slug = sanitize_title($term->slug);
$baseterm = str_replace($siteurl, '', get_term_link($term->term_id, 'product_cat'));
// Add rewrite rules for base term, pagination, and feeds
add_rewrite_rule($baseterm . '?$', 'index.php?product_cat=' . $term_slug, 'top');
add_rewrite_rule($baseterm . 'page/([0-9]{1,})/?$', 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]', 'top');
// add_rewrite_rule($baseterm . '(?:feed/)?(feed|rdf|rss|rss2|atom)/?$', 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]', 'top');

// Hook into 'init' action to set up rewrite rules
add_action('init', 'category_base_is_shop_base');

// Function to run when a new term is created
add_action('create_term', 'category_base_is_shop_base_edit_success', 10, 2);

function category_base_is_shop_base_edit_success($term_id, $taxonomy) {
// Inform the user to flush permalinks manually
add_action('admin_notices', function() {
echo '<div class="notice notice-warning is-dismissible"><p>To ensure your custom permalink structure works, flush your permalinks by going to Settings > Permalinks and clicking "Save Changes".</p></div>';

Visit your Settings > Permalinks and hit save twice to force flush/rebuilding of permalinks. Done.

Leave a Reply