Using same slug for custom post type and custom taxonomy - React to WordPress
Same slug from custom post type and custom taxonomy

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:

function register_post_type() {
		$labels = array(
			"name"          => __( 'Web Tutorials', 'textdomain' ),
			"singular_name" => __( 'Web Tutorial', 'textdomain' ),

		$args = array(
			"label"               => __( 'Web Tutorials', 'textdomain' ),
			"labels"              => $labels,
			"description"         => "",
			"public"              => true,
			"publicly_queryable"  => true,
			"show_ui"             => true,
			"show_in_rest"        => false,
			"rest_base"           => "",
			"show_in_menu"        => true,
                        "has_archive"         => 'web-tutorials',
			"exclude_from_search" => false,
			"capability_type"     => "post",
			'capabilities' => array(
				'read_post' => 'read_post'
			"map_meta_cap"        => true,
			"hierarchical"        => false,
			"rewrite"             => array( "slug" => "web-tutorials/%custom-taxonomy-name%", "with_front" => true ),
			"query_var"           => true,
			"supports"            => array( "title", "editor", "thumbnail" ),

		register_post_type( 'custom-post-type-name', $args );

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:

function register_taxonomy() {
		$labels = array(
			"name"          => __( 'Taxonomy Categories', 'textdomain' ),
			"singular_name" => __( 'Taxonomy Category', 'textdomain' ),

		$args = array(
			"label"              => __( 'Taxonomy Category', 'textdomain' ),
			"labels"             => $labels,
			"public"             => true,
			"hierarchical"       => true,
			"show_ui"            => true,
			"show_in_menu"       => true,
			"show_in_nav_menus"  => true,
			"query_var"          => true,
			"rewrite"            => array( 'slug' => 'web-tutorials' ),
			"show_admin_column"  => false,
			"show_in_rest"       => false,
			"rest_base"          => "",
			"show_in_quick_edit" => false,

        register_taxonomy( 'custom-taxonomy-name', array( 'custon-post-type-name' ), $args );

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:

add_filter('post_type_link', 'cj_update_permalink_structure', 10, 2);
function cj_update_permalink_structure( $post_link, $post )
    if ( false !== strpos( $post_link, '%custom-taxonomy-name%' ) ) {
        $taxonomy_terms = get_the_terms( $post->ID, 'custom-taxonomy-name' ); (?)
        foreach ( $taxonomy_terms as $term ) { 
            if ( ! $term->parent ) {
                $post_link = str_replace( '%custom-taxonomy-name%', $term->slug, $post_link );
    return $post_link;

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.


7 comments on “Using same slug for custom post type and custom taxonomy”

  1. The permalink structure is working perfectly however

    On my Add new custom post type page I have this error showing up under the title box:

    Warning: Invalid argument supplied for foreach() in /homepages/37/d609489269/htdocs/glassbox/wp-content/themes/Divi-Child/functions.php on line 114

    This error is refering to the ‘ foreach ( $taxonomy_terms as $term ) { ‘
    section in the add_filter code.

    Any ideas?

    1. As I see it, you don’t have terms inside of that new taxonomy.

      Try to change it to this:
      foreach ( (array) $taxonomy_terms as $term ) {

  2. Very nice snippet! Question: as for SEO…will the taxonomy be no-indexed /no follow?
    what do you recommend?

    1. function the_category_base() {
      ‘custom-post-type-name/([^/]+)/page/(\d+)/?$’, ‘index.php?custom-taxonomy-name=$matches[1]&paged=$matches[2]’, ‘top’

      add_action(‘init’, ‘the_category_base’);

  3. The new permalink structure works but I get this error on the archive page :

    Warning: Invalid argument supplied for foreach() in [PATH_TO_THE_THEME]/functions.php on line 289

    Also, the single page (the page of the article) does not work, I get a 404.

    I went to settings -> permalinks and I clicked on the save button, same errors.

Leave a Reply

Your email address will not be published. Required fields are marked *