If you want the Terms in your Custom Taxonomy to have their URL path containing their Custom Post Type slug instead of Custom Taxonomy slug without showing 404 page, that can be a little tricky, and as I found throughout the blogs and forums quite impossible.

But, fortunately, it is possible! And it is really quite simple (although it took me a whole day and a lot of nerves to figure it out).

Before going any further, note that this only works when Permalink Settings are set to Post name!

Custom Post Type and Custom Taxonomy

Lets imagine you have situation like this, you need Custom Post Type named Recipes, and you need dynamic categories (TermsChinese, Indian, Thai… So you don’t want to create Custom Taxonomy for each and every one of them, you want them to be categories inside Taxonomy, so you can create as many as you wish.

And you want to achieve URL structure like this:

http://domain.com/recipes/
http://domain.com/recipes/chinese/
http://domain.com/recipes/indian/
http://domain.com/recipes/thai/

The logical way to do it would be like this:

// register custom post type
function create_post_types() {
    register_post_type('recipes', array(
        'labels' => array(
            'name' => 'Recipes',
            'all_items' => 'All Posts'
        ),
        'public' => true
    ));
}
add_action('init', 'create_post_types');

// register taxonomy
function create_taxonomies() {
    register_taxonomy('recipes-categories', array('recipes'), array(
        'labels' => array(
            'name' => 'Recipes Categories'
        ),
        'show_ui' => true,
        'show_tagcloud' => false,
        'rewrite' => array('slug' => 'recipes')
    ));
}
add_action('init', 'create_taxonomies');

Note that when registering Custom Taxonomy we use rewrite slug same as the Custom Post Type slug.
This is important!

Then lets create some Terms in admin for that Custom Taxonomy, as we stated above (Chinese, Indian, Thai…).

Rewrite rules

That would give us the links we wanted. But now, WordPress wouldn’t exactly know what to do with them, cause Custom Types and Taxonomies may not have the same slug, and would give 404 page on those taxonomy terms you created. So, now our function comes into play:

/*
 * Replace Taxonomy slug with Post Type slug in url
 * Version: 1.1
 */
function taxonomy_slug_rewrite($wp_rewrite) {
    $rules = array();
    // get all custom taxonomies
    $taxonomies = get_taxonomies(array('_builtin' => false), 'objects');
    // get all custom post types
    $post_types = get_post_types(array('public' => true, '_builtin' => false), 'objects');
    
    foreach ($post_types as $post_type) {
        foreach ($taxonomies as $taxonomy) {
	    
            // go through all post types which this taxonomy is assigned to
            foreach ($taxonomy->object_type as $object_type) {
                
                // check if taxonomy is registered for this custom type
                if ($object_type == $post_type->rewrite['slug']) {
		    
                    // get category objects
                    $terms = get_categories(array('type' => $object_type, 'taxonomy' => $taxonomy->name, 'hide_empty' => 0));
		    
                    // make rules
                    foreach ($terms as $term) {
                        $rules[$object_type . '/' . $term->slug . '/?$'] = 'index.php?' . $term->taxonomy . '=' . $term->slug;
                    }
                }
            }
        }
    }
    // merge with global rules
    $wp_rewrite->rules = $rules + $wp_rewrite->rules;
}
add_filter('generate_rewrite_rules', 'taxonomy_slug_rewrite');

WordPress has its own nice rewriting mechanism independent from .htaccess that is processed in php before output. Here we tell WordPress what to do with the URLs we created (only those Taxonomy Terms whose parent Taxonomy has the same slug as its Custom Post Type) and where to redirect them, by adding permalinks to his rewriting object.

PHP files

So, now, all you have to do is to create .php files in your theme directory, that will print the content. The filename for the Post Type page is arhchive-{post_type}.php, for the Taxonomy page is taxonomy-{registered_taxonomy}.php, and for the single pages its single-{post_type}.php

In our case from above it would be:

  • archive-recipes.php
  • taxonomy-recipes-categories.php
  • single-recipes.php

Permalinks

After doing all this, just go to settings/permalinks and save changes using structure /%postname%/, and thats it!

Download example files

Download working example to see how it works.