I am using woocommerce and have some 'product attributes' which are just a taxonomies.

I have the following taxonomies:

  • pa_color
  • pa_material
  • pa_style

the pa_ stands for product attribute and it is built-in to WooCommerce handles these taxonomies, so I can't change that.

WooCommerce also comes with product categories (product_cat) and product tags (product_tag) taxonomies.

the following query gets me all the Red, Vinyl products in the Fabrics product category


I'd like to turn this into a pretty permalink, something like:


This answer gets me pretty close to what I am trying to achieve:

URL rewrite rules for multiple taxonomies query

in that I can get my URL to handle 2 taxonomies (product_cat + 1 attribute taxonomy), but I am stuck on how to get 3 (or more) parameters.

So I can get:




but not all 3 together


edit / though i think it'd be ideal to have the URL be


i'm not sure that is possible?

end edit/

WooCommerce also has some settings for modifying the permalinks (like prepending the URL with 'shop' or changing the product-category slug) so i'll need to account for that eventually, but right now I need to get this first step down.

for simplicity's sake, this is a stripped down version of my code. I'm actually getting the taxonomies from built-in woocommerce functions, but the question isn't limited to woocommerce... as it pertains to any site w/ a lot of taxonomies.

add_action( 'init', 'wpa_init' );
function wpa_init() {

  $taxonomies = array(
    'pa_color' => 'Color',
    'pa_material' => 'Material',
    'pa_style' => 'Style',
  $base = 'product-category';

  foreach ( $taxonomies as $taxonomy => $mask ):
    $attribute = strtolower( sanitize_title( $mask ) );
      $base.'/(.+?)/' . $attribute . '/([^/]+)(/page/(\d+))?/?$',
      'index.php?product_cat=$matches[1]&' . $taxonomy . '=$matches[2]&paged=$matches[4]',

this was the best resource i've found on taxonomy query permalinks: http://thereforei.am/2011/10/28/advanced-taxonomy-queries-with-pretty-urls/

but it, too, seems stuck at 2 taxonomies.

2 Answers 2


You could try something like this:

function custom_rewrite( $wp_rewrite ) {

    $feed_rules = array(
        'product-category/(.+)/material/(.+)/color/(.+)'    =>  'index.php?product_cat='. $wp_rewrite->preg_index(1).'&pa_material='. $wp_rewrite->preg_index(2).'&pa_color='. $wp_rewrite->preg_index(3)
        'product-category/(.+)/color/(.+)'    =>  'index.php?product_cat='. $wp_rewrite->preg_index(1).'&pa_color='. $wp_rewrite->preg_index(2)
        'product-category/(.+)/material/(.+)'    =>  'index.php?product_cat='. $wp_rewrite->preg_index(1).'&pa_material='. $wp_rewrite->preg_index(2)

    $wp_rewrite->rules = $feed_rules + $wp_rewrite->rules;
// refresh/flush permalinks in the dashboard if this is changed in any way
add_filter( 'generate_rewrite_rules', 'custom_rewrite' );

It may not work exactly as advertised, and you might need to do some regex modifications, but that's the general gist of what needs to be done (90% of the work).

You will need to flush the permalinks if you add/edit/remove this code, put it in functions.php or a custom plugin. You can flush permalinks by simply going into the admin area and re-saving the permalinks

As a sidenote, you may run into clashing issues if you start using heirarchies,

e.g. if you have a tshirts/small and a dresses/small category, and you use a URL such as /products-category/small/color/red you might not get the results you expected, e.g. small tshirts? or did you mean small dresses?

So beware of ambiguity

  • works pretty well! ideally, i'd like to figure out a way to loop through this so that i don't have to hand-code each new rule. could you explain the difference in matching between: (.+?) ([^/]+) and (.+) ? Commented Mar 7, 2012 at 19:04
  • They're all regex, the particulars of which I'm not confident enough to answer, but if you find somewhere that explains it well please do link me to it!
    – Tom J Nowell
    Commented Mar 7, 2012 at 23:53
  • i still think it's VOODOO! thanks so much for answering... Commented Mar 8, 2012 at 0:42
  • came back to this- should point out that generate_rewrite_rules is not a filter: it is an action where the parameter is passed by reference Commented Mar 18, 2012 at 19:25
  • All actions are filters if you look closely enough =p
    – Tom J Nowell
    Commented Mar 19, 2012 at 19:48

For future reference (like next year when I google and find my own question again) this can be converted into add_rewrite_rule() syntax by converting the keys in the $new_rules array into the $regex parameter of add_rewrite_rule() with the value as the $rewrite and 'top' as the $position.

add_rewrite_rule($regex,$rewrite, $position);

Or as an example of one of my requests from above:

function custom_rewrite() {


// refresh/flush permalinks in the dashboard if this is changed in any way
add_action( 'init', 'custom_rewrite' );

For some reason I was unsuccessful using regenerate_rewrite_rules as a hook/filter so switched to init.


Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.