Wordpress sometimes is plain limiting, and some of its logics I just don't get...

For example if I have two parent directories:

  • parent1
  • parent2

with subcategories:

  • parent1
    • sub
  • parent2
    • sub

I cannot have sub sub as slug or name... WordPress automatically appends what it thinks is best like sub-1 or sub-parent2. This is not very beautiful. And I consider this nothing short of a bug.

3 Answers 3


Short answer: No - it can't be done.

Long answer: Category slugs must be unique, regardless of parent. Although quite a few people like to call this a bug, it's actually by design. Basically, since categories can be moved around at will, the slugs must be unique. Sure, it's nested properly now, but if you decide at some point to do away with your hierarchy and move to a flat category structure, you'll have duplicate URLs (instead of cat1/slug, cat2/slug you'll have /slug and /slug). To avoid people unintentionally breaking their sites, they've enforced this restriction.

If you do hack the database to force duplicate slugs, it won't matter - if you navigate to a page that has a duplicate slug, it will load the first instance of that slug in the database no matter what. So if your first 'sub' is category ID 5 and your second 'sub' is category ID 20, and you force them to have the same slug, any time you go to category 20, it will load category 5.

The only time I've seen this successfully overridden is with WPML (so subdomain languages can have the same slug - en/slug, fr/slug, de/slug, etc), and they took MONTHS to make it work - even then it still has issues.

  • 1
    Six years on and this is still a relevant answer. :-)
    – Craig
    Jul 7, 2018 at 21:43

We decided to launch some sites in Wordpress/Woocommerce instead of Magento V2 as it's very resource heavy, we assumed that the duplicate category problem in Wordpress had been resolved the past years since we last needed to do this, it hasn't.

Referring to "Short answer: No - it can't be done.", this is correct for everyone, except Google doesn't care about that, we needed product categories "retailer/brandabc" and "brand/brandabc", both the same permalink but with different parents, not possible out of the box however the problem is, Google will lower ranking if you have retailer/brandabc1 and brand/brandabc2, these small changes can make all the difference.

We know this because a 15,000product Magento V2 site without marketing, without advertising, Google ranked over 60% of the site pages in a month, the small things can make all the difference, we needed to replicate it for Wordpress and no was not an option.

Some of the consultants found a way to do it, you can go to MochiStyles.com/retailer/zaful and MochiStyles.com/brand/zaful with both having the same end url, it works for both categories and product categories, but is only part of the build packages. For Woocommerce you can use plugins like Permalink Manager to remove the product and product-category prefixes.

In the end you need to restructure your site categories, it will lower the ranking but Wordpress is a content system, trying to automate it needs corporate level technology, which is what we use, it is a limitation of Wordpress from the original design.


I needed a solution for this and responded to a similar question here,

WordPress won't allow you to have duplicate term slugs in the same taxonomy, they need to be unique in the database. However, we can extend WordPress core to make the url structure work while maintaining the unique term slugs.

For example: category/books/hobbit and category/movies/hobbit urls where books_hobbit and movies_hobbit are the unique child terms slugs and their parent term slugs are books and movies, respectively.

First, the rewrite rule:

 * Add custom rewrite rules for parent/child term urls.
 * Adds a rewrite rule that will match terms 
 * that have their slug prefixed with the parent term
 * slug, separated by an underscore, but their url
 * is just the child term slug.
function prefix_custom_hierarchical_taxonomy_rewrite_rules() {

    $tax_object = get_taxonomy( 'category' );

    if ( $tax_object ) {
        $tax_slug = $tax_object->rewrite['slug'];
            $tax_object->rewrite['slug'] . '/([^/]+)/([^/]+)/?',
            'index.php?' . $tax_object->query_var . '=$matches[1]_$matches[2]',
add_action( 'init', 'prefix_custom_hierarchical_taxonomy_rewrite_rules', 100 );

Then filter the term_link such that you remove the {parent-slug}_ part of the slug in the final child term's link:

 * Filter the term link to handle hierarchical taxonomy permalinks.
 * This will filter the term link to remove the parent term slug
 * from the child term url, so that the url is just the child term slug,
 * where the child term name is "parent_child" and the parent term name is "parent".
 * @param string $link The term link
 * @param WP_Term $term The term object
 * @param string $taxonomy The taxonomy name
 * @return string
function prefix_custom_term_link( $link, $term, $taxonomy ) {

    if ( $taxonomy !== 'category' ) {
        return $link;

    // Get the parent term
    $parent_term = get_term( $term->parent, $taxonomy );

    if ( $parent_term && ! is_wp_error( $parent_term ) ) {
        // Remove the "parent_" prefix from the child term slug
        // to generate a url that looks like /parent/child/
        $link = str_replace( '/' . $term->slug, '/' . str_replace ( $parent_term->slug . '_', '', $term->slug ), $link );

    return $link;
add_filter( 'term_link', 'prefix_custom_term_link', 10, 3 );

You may also want to filter the parse_query to check for any child terms that don't follow this structure. This avoids having to set all your child category slugs to the parent_child pattern, you can just apply it to those that have a "duplicated" url slug.

 * Parse the query to handle hierarchical taxonomy permalinks.
 * This filter the query to check for terms that are child terms
 * yet do not have the parent term in the slug, thus the parent/child
 * structure should literally look for a "child" slug, not "parent_child"
 * @param WP_Query $query The WP_Query instance (passed by reference)
 * @return void
function prefix_custom_parse_query($query) {
    if ( isset( $query->query_vars['category_name'] ) ) {
        $slug = $query->query_vars['category_name'];
        if ( strpos( $slug, '_' ) !== false ) {
            list( $parent, $child ) = explode( '_', $slug, 2 );
            if ( ! term_exists( $slug, 'category' ) && term_exists( $child, 'category' ) ) {
                $query->query_vars['category_name'] = $child;
add_action( 'parse_query', 'prefix_custom_parse_query' );

I've put this into a gist here.


  • This only works with hierarchical taxonomies, in this example we're using the built-in category taxonomy
  • Update the prefix_ naming to something unique to your theme or plugin.
  • This example uses a custom term slug for your child terms that follows a pattern like {parent-slug}_{child-slug}. You could change that to another separating character, but be careful to use a dash - as that is the default used by core when names are generated (spaces turn to dashes).
  • This requires the "pretty" permalinks where /%postname%/ is set in the Settings > Permalinks.

Your Answer

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