Skip to main content

Using same slug for custom post type and custom taxonomy

It is fairly common that you, as a website manager, would like to use the same slug for custom post type as for custom taxonomy, but it is not a simple thing to do if you are not familiar with WordPress permalink structures.

I will explain, say your archive page is – that is the page that will hold all of your posts under the web-tutorials custom post type. Now you want your web-tutorial(a singular post) URL to be

web-tutorials Will be your custom post type slug.

taxonomy-category Your taxonomy category.

post-name Will be your post name/title.

It is impossible to do so without coding customizations, and to my surprise, it is a simple one.

Let us start by creating  a custom post type with the id of custom-post-type-name:

In line 17 I’m creating the slug for our custom post archive page (web-tutorials), and in line 25, I’m creating the default slug for any post that is under the custom-post-type-name post type. Very easy to overlook but it is crucial for it to succeed, is to use in between the % the exact taxonomy name, in our case it is custom-taxonomy-name.

Let’s continue with creating a custom taxonomy with the previously mentioned id, custom-taxonomy-name:

As I’m finishing creating the taxonomy, our little project is almost done. Now, the last thing that is left is to change all of our permalinks to the correct URL structure. We will do it by hooking to post_type_link filter:

The filter gives us two variables; the first one is the $post_link and second is the $post data itself.

I’m checking if the $post_link contains %custom-taxonomy-name%, if it does, I’m getting all the terms for that specific post id with the taxonomy custom-taxonomy-name and rotating through each of them until I get the parent. Next, I’m adjusting the $post_link with the term’s slug.

Lastly, I’m returning the $post_link back to the filter.

That’s it. Now we have the exact URL structure we wanted to achieve, fast and easy.

  • ArtPenley

    This doesn’t seem to work for more than one CPT?

    • Kirill Lavrishev

      Sorry, Just seen the notification about the comment.

      Can you share your code please? are you adjusting

      as well?

      • ArtPenley

        My code varies slightly to yours but as far as I can see I added all the necessary elements:

        ‘singular_name’ => ‘Songs’,
        ‘add_new’ => ‘Add New’,
        ‘add_new_item’ => ‘Add New Song’,
        ‘edit_item’ => ‘Edit Song’,
        ‘new_item’ => ‘New Song’,
        ‘all_items’ => ‘All Songs’,
        ‘view_item’ => ‘View Song’,
        ‘search_items’ => ‘Search Songs’,
        ‘not_found’ => ‘No Songs Found’,
        ‘not_found_in_trash’ => ‘No Songs found in Trash’,
        ‘parent_item_colon’ => ”,
        ‘menu_name’ => ‘Songs’,

        $args = array(
        ‘labels’ => $labels,
        ‘public’ => true,
        ‘has_archive’ => ‘music’,
        ‘show_ui’ => true,
        ‘capability_type’ => ‘post’,
        ‘hierarchical’ => true,
        ‘query_var’ => true,
        ‘show_in_menu’ => true,
        ‘menu_icon’ => ‘dashicons-format-audio’,
        ‘taxonomies’ => array( ‘song_categories’),
        ‘taxonomies’ => array(‘songs’, ‘post_tag’),
        ‘rewrite’ => array(
        ‘slug’ => ‘music/%song_categories%’,
        ‘with_front’ => false,
        ‘supports’ => array(
        register_post_type( ‘songs’, $args );

        ‘song_categories’, ‘songs’,
        ‘hierarchical’ => true,
        ‘label’ => ‘Categories’,
        ‘query_var’ => true,
        ‘show_ui’ => true,
        ‘show_tagcloud’ => false,
        ‘rewrite’ => array(
        ‘slug’ => ‘music’,
        ‘with_front’ => false,

        add_action( ‘init’, ‘songs_init’ );