This is driving me nuts and I'm sure it's simple but nothing I search for comes up with a simple structure (everything is very complex).

I have a custom post type product_listing and a custom taxonomy of product_cat (which is hierarchical and should be have like categories).

I simply want my URLs to look like this:


But for the life of me, no matter what I do, I'm getting the dreaded 404 issue. Pages work okay and Posts work okay but my custom posts don't work correctly. They're showing up as:


Which actually works! It's just that I want to see my custom taxonomy in there plus I want to be able to access the taxonomy.php template I have setup by going to:


None of my slugs are the same, nor do I want them to be. Here is the post type and taxonomy part of my functions.php file:


// register the new post type
register_post_type( 'product_listing', array( 
    'labels'                 => array(
        'name'               => __( 'Products' ),
        'singular_name'      => __( 'Product' ),
        'add_new'            => __( 'Add New' ),
        'add_new_item'       => __( 'Create New Product' ),
        'edit'               => __( 'Edit' ),
        'edit_item'          => __( 'Edit Product' ),
        'new_item'           => __( 'New Product' ),
        'view'               => __( 'View Products' ),
        'view_item'          => __( 'View Product' ),
        'search_items'       => __( 'Search Products' ),
        'not_found'          => __( 'No products found' ),
        'not_found_in_trash' => __( 'No products found in trash' ),
        'parent'             => __( 'Parent Product' ),
    'description'           => __( 'This is where you can create new products on your site.' ),
    'public'                => true,
    'show_ui'               => true,
    'capability_type'       => 'post',
    'publicly_queryable'    => true,
    'exclude_from_search'   => false,
    'menu_position'         => 2,
    'menu_icon'             => get_stylesheet_directory_uri() . '/images/tag_orange.png',
    'hierarchical'          => true,
    '_builtin'              => false, // It's a custom post type, not built in!
    'rewrite'               => array( 'slug' => 'products', 'with_front' => true ),
    'query_var'             => true,
    'supports'              => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions' ),
) );

//hook into the init action and call create_book_taxonomies when it fires
add_action( 'init', 'create_product_taxonomies', 0 );
//add_action('admin_init', 'flush_rewrite_rules');

//create two taxonomies, genres and writers for the post type "book"
function create_product_taxonomies() {
    // Add new taxonomy, make it hierarchical (like categories)
    $labels = array(
        'name'              => _x( 'Categories', 'taxonomy general name' ),
        'singular_name'     => _x( 'Category', 'taxonomy singular name' ),
        'search_items'      =>  __( 'Search Categories' ),
        'all_items'         => __( 'All Categories' ),
        'parent_item'       => __( 'Parent Categories' ),
        'parent_item_colon' => __( 'Parent Categories:' ),
        'edit_item'         => __( 'Edit Category' ), 
        'update_item'       => __( 'Update Category' ),
        'add_new_item'      => __( 'Add New Category' ),
        'new_item_name'     => __( 'New Category Name' ),
        'menu_name'         => __( 'Category' ),

    register_taxonomy( 'product_cat', array( 'product_listing' ), array(
        'hierarchical'  => true,
        'labels'        => $labels,
        'show_ui'       => true,
        'query_var'     => true,
        //'rewrite'     => true,
        'rewrite'       => array( 'slug' => '%category%', 'with_front' => true ),
    ) );

    // Add new taxonomy, NOT hierarchical (like tags)
    $labels = array(
        'name'                       => _x( 'Scents', 'taxonomy general name' ),
        'singular_name'              => _x( 'Scent', 'taxonomy singular name' ),
        'search_items'               =>  __( 'Search Scents' ),
        'popular_items'              => __( 'Popular Scents' ),
        'all_items'                  => __( 'All Scents' ),
        'parent_item'                => null,
        'parent_item_colon'          => null,
        'edit_item'                  => __( 'Edit Scent' ), 
        'update_item'                => __( 'Update Scent' ),
        'add_new_item'               => __( 'Add New Scent' ),
        'new_item_name'              => __( 'New Scent Name' ),
        'separate_items_with_commas' => __( 'Separate scents with commas' ),
        'add_or_remove_items'        => __( 'Add or remove scents' ),
        'choose_from_most_used'      => __( 'Choose from the most used scents' ),
        'menu_name'                  => __( 'Scents' ),

    register_taxonomy( 'scent', 'product_listing', array(
        'hierarchical'  => false,
        'labels'        => $labels,
        'show_ui'       => true,
        'query_var'     => true,
        //'rewrite'     => array( 'slug' => 'scents' ),
    ) );

I also have another custom taxonomy of scents that I'd ideally like to have some kind of friendly url but I'm more open on this. I'd like to maybe access a list of all scents by going to mysite.com/products/scents but they don't have to be category specific.

Can anybody help me?

5 Answers 5


Change slug in your post type arguments to products/%product_cat%, and slug in your taxonomy arguments to just products, then flush your rewrite rules. WordPress should now handle /products/my-product-cat/post-name/!

Now finally, we need to help WordPress a little with generating permalinks (out of the box, it won't recognise the permastruct tag %product_cat%):

 * Inject term slug into custom post type permastruct.
 * @link   http://wordpress.stackexchange.com/a/5313/1685
 * @param  string  $link
 * @param  WP_Post $post 
 * @return array
function wpse_5308_post_type_link( $link, $post ) {
    if ( $post->post_type === 'product_listing' ) {
        if ( $terms = get_the_terms( $post->ID, 'product_cat' ) )
            $link = str_replace( '%product_cat%', current( $terms )->slug, $link );

    return $link;

add_filter( 'post_type_link', 'wpse_5308_post_type_link', 10, 2 );

One thing to note, this will just grab the first product category for the post ordered by name. If you're assigning multiple categories to a single product, I can easily change how it determines which one to use in the permalink.

Lemme know how you get on with this, and we can tackle the other issues!

  • Oh WOW I'm in awe! This WORKED! Finally! I didn't ever think anything would!!! thank you SOOOOOO much!!! ............ Now, how would I generate a link (something like the_permalink) to grab the taxonomy url on its own? /products/my-product-cat/ Commented Dec 14, 2010 at 3:21
  • I think I got that worked out^^^ ............ but now I'm stuck on Pagination. Seems to break when using any pagination (custom function or builtin) as /products/my-product-cat/ looks okay but /products/my-product-cat/page/2/ returns a 404 and drops the taxonomy.php file in favor of the index.php file. I've tried everything I could find and still nothing. Commented Dec 14, 2010 at 6:45
  • 1
    Would you consider changing the single product rewrites to product/cat-name/product-name? (note the singularity) The trouble is the rewrite rule for the taxonomy pagination isn't triggered, because it's getting caught by an earlier rule for single products! Commented Dec 14, 2010 at 10:52
  • I'm open to it. But to wrap my head around it, would that mean products would have a different URL path if you're viewing a single product page vs. a 'category' page? So single would be product/cat-name/prod-name but cats would be products/cat-name/ ? Wouldn't that sort of defeat the purpose of "human friendly" urls? I have a hard time helping my clients learn the wp dashboard let alone that difference... If I'm misunderstanding pls let me know! I might also be happy with NO front directory, such as product-cat/product-nam and just product-cat/. Could that work? Commented Dec 14, 2010 at 14:44
  • 2
    You got it - though I'd say it is "human friendly", as there is a clear distinction between archives (products/) and singular items (product/). And no, I'd keep the 'front directory' - it helps keep a clear distinction from posts and pages, plus otherwise you might end up with performance issues (i.e. verbose rewrite rules). Commented Dec 14, 2010 at 15:31

Thanks @TheDeadMechanic, your answer helped me out, but only partially. I wanted to do the same thing @RodeoRamsey asked for, but with nested categories (ie: mysite.com/products/category1/child-category-1/grandchild-category-1/product-name) and your solution didn't work for that.

I finally came up with an extended solution to my question that works, so if anyone else needs nested categories/subcategories you can see a detailed solution on my own question. Hope it helps others, and thanks for the initial steps.


I'm not sure wp supports that structure out of the box - but you can very easily create your own rewrite rules to do so.

Check out a previous answer here Author url rewrite.

You can change the line

$newrules['author/([^/]+)/songs/?$'] = 'index.php?post_type=songs&author=$matches[1]';

to something like

$newrules['products/([^/]+)/([^/]+)/?$'] = 'index.php?post_type=product_listing&product_cat=$matches[1]&name=$matches[2]';

the product_cat part here may be superfluous - I am not sure if it is needed.

You can add any rules you like and they will have priority over the inbuilt ones.

  • Well isn't that interesting. Oh boy, I think custom rewrite rules are beyond me. I tried the above code (and the rest of it from the other post) and nothing changed. I flushed everything and then tried again and still no change, so I commented out all the rewrite rules set in the custom post types and taxonomies and flushed and STILL nothing. Commented Dec 14, 2010 at 1:09

Yeah it was driving me crazy before setting the permalink for custom post type. I found a plugin to handle the custom post type. It's very easy to use. http://wordpress.org/extend/plugins/custom-post-permalinks/ WP should add this as a core feature! Leo

  • I had seen this one before and refrained from using it because it said "for non-hierarchical" taxonomies. I had one that was set to hierarchical so I didn't think it would work, but so far, it seems to have done the trick! Plus, it seems to be working to accomplish the /products/cat-name/prod-name/ structure I was trying to achieve (see comment to other answer). @TheDeadMedic, is this a viable option? Or should I stick with the rewrites in the functions.php file? Commented Dec 14, 2010 at 17:57

Actually, it's pretty easy. You just need one line. Here's my code

function create_product_taxonomies()
// Add new taxonomy, make it hierarchical (like categories)
    $labels = array(
        'name' => _x('Categories', 'taxonomy general name'),
        'singular_name' => _x('Category', 'taxonomy singular name'),
        'search_items' => __('Search Categories'),
        'all_items' => __('All Categories'),
        'parent_item' => __('Parent Categories'),
        'parent_item_colon' => __('Parent Categories:'),
        'edit_item' => __('Edit Category'),
        'update_item' => __('Update Category'),
        'add_new_item' => __('Add New Category'),
        'new_item_name' => __('New Category Name'),
        'menu_name' => __('Category'),

    register_taxonomy('product_cat', array('product_listing'), array(
        'hierarchical' => true,
        'labels' => $labels,
        'show_ui' => true,
        'query_var' => true,
        'rewrite' => array('hierarchical' => true),

And applied to the generated taxonomy for my Reviews CPT from GenerateWP.com. I'm using this on my own WordPress site, https://www.wpstarters.com

function reviews_category_taxonomy() {

    $labels = array(
        'name'                       => _x( 'Reviews Categories', 'Taxonomy General Name', 'reviews_category' ),
        'singular_name'              => _x( 'Reviews Category', 'Taxonomy Singular Name', 'reviews_category' ),
        'menu_name'                  => __( 'Reviews Category', 'reviews_category' ),
        'all_items'                  => __( 'All Review Categories', 'reviews_category' ),
        'parent_item'                => __( 'Parent Review Category', 'reviews_category' ),
        'parent_item_colon'          => __( 'Parent Review Category:', 'reviews_category' ),
        'new_item_name'              => __( 'New Review Category Name', 'reviews_category' ),
        'add_new_item'               => __( 'Add New Review Category', 'reviews_category' ),
        'edit_item'                  => __( 'Edit Review Category', 'reviews_category' ),
        'update_item'                => __( 'Update Review Category', 'reviews_category' ),
        'view_item'                  => __( 'View Review Category', 'reviews_category' ),
        'separate_items_with_commas' => __( 'Separate items with commas', 'reviews_category' ),
        'add_or_remove_items'        => __( 'Add or remove items', 'reviews_category' ),
        'choose_from_most_used'      => __( 'Choose from the most used', 'reviews_category' ),
        'popular_items'              => __( 'Popular Review Categories', 'reviews_category' ),
        'search_items'               => __( 'Search Items', 'reviews_category' ),
        'not_found'                  => __( 'Not Found', 'reviews_category' ),
        'no_terms'                   => __( 'No Review Categories', 'reviews_category' ),
        'items_list'                 => __( 'Review Categories list', 'reviews_category' ),
        'items_list_navigation'      => __( 'Review Categories list navigation', 'reviews_category' ),
    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => true,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => false,
        'show_in_rest'               => true,
        'rewrite' => array( 'hierarchical' => true ),
    register_taxonomy( 'reviews_category', array( 'wps_reviews' ), $args );

add_action( 'init', 'reviews_category_taxonomy', 0 );

All you need it so put the 'rewrite' => array('hierarchical' => true),

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.