I need to use a dynamic permalink structure for WooCommerce products, basically, the structure I'm trying to achieve is the following: /<dynamic>/post_name.

So what I did is create a rewrite tag:

add_action('init', function () {
  add_rewrite_tag('%custom_string_only_for_products%', '(.+)', 'custom_part_of_product=');

add_filter('available_permalink_structure_tags', function (array $tags): array {
  $tags['custom_string_only_for_products'] = 'some custom_string_only_for_products thing';
  return $tags;

I've assigned the tag above only for the products permalink:

enter image description here

In order to change the permalink structure I used the post_type_link hook, essentially, the dynamic part must change depending on the category assigned to the product, eg:

add_filter('post_type_link', function (string $permalink, WP_Post $post): string {

  if (false === strpos($permalink, '%custom_string_only_for_products%')) {
    return $permalink;

  if ($post->post_type != 'product') {
    return $permalink;

  $post_id = $post->ID;
  $custom_part = '';

  // watches
  $is_watch = (has_term('watches', 'product_cat', $post_id));
  if ($is_watch) {
    $brand = get_the_terms($post_id, 'product_brand');
    return custom_permalink_rewrite("watches/", $permalink, $brand);

  return $permalink;
}, 1, 2);

function custom_permalink_rewrite($custom_part, $permalink, $brand = null)
  if (isset($brand[0])) {
    $brand = isset($brand[0]) ? sanitize_title($brand[0]->name) : '';
    $custom_part .= $brand;
  return str_replace('%custom_string_only_for_products%', $custom_part, $permalink);

The function above check if the current $post is a product, if so, check if the product have a specific category and then rewrite the dynamic part of the permalink using a custom taxonomy product_brand.

So in the case above, if the product have the category watches, and the brand rolex is assigned, the final result will be:


Until here all is working great but for some reason now, I can't load the pages. Seems that I'm obliged to add a static word on the permalink product otherwise Wordpress will load any page as front page, which is kinda weird, eg:

enter image description here

The main problem here is that my customer doesn't want the /product prefix, so I came up with another solution, within the post_type_link hook I wrote:

$permalink = str_replace('product/, '', $permalink);

and then I added this code within the init hook:

add_action('init', function () {
  $rewrite_obj = new WP_Rewrite();
  $rewrites    = $rewrite_obj->wp_rewrite_rules();
  $current_state = md5(json_encode($rewrites));
  $stored_state  = get_option('wpse_421338_rewrite_state', '');

  if ($current_state !== $stored_state) {
    $products = wc_get_products([
      'limit' => -1

    foreach ($products as $prod) {
      $id = $prod->get_id();
      $permalink = $prod->get_permalink();
      $url_scheme = parse_url($permalink);
      $new_url = ltrim($url_scheme['path'], '/');

        'index.php?post_type=product&p=' . $id . '&prod_brand=&matches[1]&sku=$matches[2]',

    update_option('wpse_421338_rewrite_state', $current_state);

the code above create the rewrite rules necessary to assign the new url to the actual product, so Wordpress knows which product load for that specific url. Since the flush_rewrite_rules is an expensive computational operation, I tried to flush the permalinks only when the state changes, using a custom option.

The problem's that the site performance now are dropped ..

Is there anyone who somehow managed to get a dynamic permalink as an initial part for woocommerce products only?


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.