WordPress permalinks are great for having friendly urls that your visitors can also remember and that benefit your SEO optimization. Some solutions will require you to have an url made of a combination of different content. In this tutorial we will look how to have a custom taxonomy and also append the custom post type slug to the end of it.

Reason for a Custom WordPress Rewrite Rule

Let’s look at a scenario to better understand the reason for this. We have a custom post type Resources that also needs a custom taxonomy Type so that we can categorize them under different types of resource. We will display the resources under their category. When our visitors visit an url www.yoursite.com/resources, they will get a list of resources that are categorized as Featured.

If our visitor visits another url such as www.yoursite.com/resources/e-books they will get a list of all our resources of type e-books.

So, you do get it right? Our main slug in the url is actually a slug for the taxonomy and not our post type. But, when our visitor clicks on an e-book, they will get to an url such as www.yoursite.com/resources/e-books/my-new-book.

The my-new-book is a slug for our Resource.

Let’s learn now how to accomplish that.

The Custom Post Type

Our custom post type will be a resource_post_type. This ensures that the WordPress does not look for the post type when on url /resources/. We will create our own custom WordPress rewrite rule to handle our resources.

The only part that interests us here is the taxonomies key that defined a relationship with a taxonomy. Let’s make that taxonomy now.

The Custom Taxonomy

The taxonomy will be registered for our post type Resource. We will also add a rewrite rule immediately when registering it. This will ensure that the WordPress does look for the taxonomy Resource Type when visiting the url /resources/.

The WordPress rewrite rule is created under the parameter rewrite. Now we have the url structure set for our taxonomies to be displayed when visiting resources/ and any sub page such as resources/e-books.

The Custom WordPress Rewrite Rule

Now we need to get on the business and create the WordPress rewrite rule to handle third level slugs such as resources/e-books/my-new-book.

We are hooking our function resources_cpt_generating_rule to the filter generate_rewrite_rules. This filter will be called when flushing rewrite rules. I recommend you go to Settings > Permalinks and just save them once again to flush the rewrite rules.

When our WordPress rewrite rule is being generated, we are getting all our terms from the Resource Type taxonomy. Even those that do not contain any resource relationship. Do take note that you will have to save the permalinks when creating a new taxonomy term, just in case.

Once we get all the terms, we iterate through all of them and create a WordPress rewrite rule for each. The rewrite rule will say something like this:

When you visit resources/e-books/my-new-book, get the third level slug my-new-book and set the query string to look for a post type resources_post_type with the slug (name) my-new-book.

The Custom Post Type Link

So now we have the url structure all set with our new WordPress rewrite rule. But what happens when you save that Resource? The link that is generated once we save our new Resource will be something like resources_post_type/my-new-book. This is not right.

It should be resources/e-books/my-new-book. This can be done using the filter post_type_link.

Here we are checking if the post is of type resources_post_type. This will ensure that we create a different post link only for our Resources. We get all the terms from our custom taxonomy, but only for the current Resource.

While we iterate through all of them, we escape the slug featured because this is just a secondary type. You don’t have to do that, but if you have a secondary type, don’t include them for the link. Use only the main one.

When we set a term slug, we break out of the loop. We then create a permalink structure that follows our custom WordPress rewrite rule and return it from that filter.

Now you also have a good structured link when saving your Resource.


In this tutorial we have learned how to create custom WordPress rewrite rules and also make a relationship between different content in the url structure. This could benefit the SEO also but more importantly, your visitors will have a really friendly url for the resources they want to see or read.

Posted by Igor Benic

Web Developer who mainly uses WordPress for projects. Working on various project through Codeable & Toptal. Author of several ebooks at https://leanpub.com/u/igorbenic.


  1. Good post, it’s very interesting

    I have a problem with this from the permalink. How can I make the permalinks show me this route: resources/e-books/history/my-new-book and this other route: resources/e-books/history/modern/my-new-book
    I’ve been looking for the solution for a few days but I still have not found anything, I would appreciate your help.



    1. Hi Iñigo, I am glad you like the article!

      You could use the similar code I have written there, but you would need to have some more loops. You can loop through the terms (parent) and then also get the children terms from each parent. Then you can loop through them and set them like this in the code.

      You could try something like this: https://gist.github.com/igorbenic/3ba41de1db69bf028651e98b4615cfee


  2. Hi Igor,

    Thank you very much for your answer

    I have tried the new code and it does not work for me, it still does the same thing as before but now the single- {resources_post_type} .php gives 404 error, I have updated the permalinks but nothing.

    My structure is:
    – Category 1
    – Category 1.1
    – Category 1.1.1
    – Resource 1
    – Resource 2
    – Resource 3
    – Category 1.1.2
    – Resource 4
    – Resource 5
    – Resource 6
    – Category 1.1.3
    – Resource 7
    – Resource 8
    – Resource 9
    – Category 1.1.4
    – Resource 10
    – Resource 11
    – Resource 12
    – Category 1.2
    – Resource 13
    – Resource 14
    – Resource 15
    – Category 1.3
    – Category 2
    – Category 2.1
    – Category 2.2
    – Category 2.3

    With your code when i go to:
    category 1 -> resources/categoria-1/ OK
    category 1.1 -> resources/categoria-1-1/ ERROR should be resources/categoria-1/categoria-1-1/ and projects of category 1.1.1
    resource 1 -> resources/categoria-1-1-1/resource-1/ ERROR should be resources/categoria-1/categoria-1-1/categoria-1-1-1/resource-1/ and return error 404

    Any ideas?
    Thank you very much for your help.


    1. I have changed the post type into ‘resources’ instead of ‘resources_post_type’. If you change the $post_type back to resources_post_type, would that work? What post type slug are you using?


    1. I have edited your comment with the link to the code so that we don’t waste space here if other readers want to read the comments.

      I will have to get back to you later today or tomorrow on this since this is something I have to create and test.:)


      1. Thanks a lot.
        In github it is much cleaner


        1. I have wrapped it a little: https://gist.github.com/igorbenic/3ba41de1db69bf028651e98b4615cfee#file-solution-php

          This will work for the category/resource-content

          For something like category/category-2/resource-content, I would need a little more time on this to check. This is a little harder to achieve because WordPress then won’t know if category/category-2 is a resource or a resource_type since category/resource-content is also possible.

          That is why even core WordPress won’t give you the ability to have category-1/category-2


  3. Hi Igor,

    thanks for your help, I did not get what you said either, the core of wp is closed with this theme.
    I have managed to make it work but I do not think it is a correct solution because it does not recognize the single-{post-type} and i need to use templates to show the single post.




    1. You should check what is wrong with it, which template does it include. I assume this could actually see the pages as taxonomies and not singular pages. That is why I said it’s a bit hard to get two categories one below the other. If for some pattern you would like to look at it as a singular page (single-{post-type}.php), then you should maybe exclude the taxonomy slug in the rewrite rule.


  4. Hi Igor, great tutorial! I have been looking for something like this for a while now and I have my permalinks working perfectly thanks to what you have done.

    Here is my question. In your tutorial you have the resources page set to show only featured resource types. What if I wanted to use a proper resources-archive template with pagination? At the moment I am having to create a page called “resources” and give that a template with a custom WP_Query object on it but it would be better if I could bring the default archive functionality into play.



    1. Never mind, think I worked it out. I set “has_archive” to true for the CPT then I wrote a header redirect that redirects the default slug (example.com/resource_post_type) for the post type to “example.com/resources” and then wrote a rewrite rule that fetches the correct post type when on that url.

      So now all of the following work:

      http://www.example.com/resources/e-books/my-new-book – loads the correct post
      http://www.example.com/resources/e-books – loads a taxonomy archive of all posts with a resource type of ebook
      http://www.example.com/resources – loads all posts that are resource custom posts

      Really pleased with this! One of those things I’ve been meaning to get to grips with for ages and thanks to this article was finally able to crack it!


      1. Thank you, Gareth for the kind words. I am really glad you liked the tutorial and that it has helped you. Also, thank you very much for the examples you have posted here with explanations on what you have achieved with it!

        I am sure it will help others who stumble upon this:)


  5. Hi, Igor, thanks for the post. I did implement it on a project (still local) that I’m working on.

    I however ran into a bit of trouble. With “The Custom Post Type Link” example used and edited to fit my parameters, I no longer have the ability to edit the slug, the remaining part with the post name, in fact, WordPress does not automatically write out this part. So that except, I manually edit the slug field from the Screen Options settings then I have no complete url for a post and then I get 404.

    I wonder if my explanation is clear enough and I wish I had a way to post screenshots and code to you.

    Could you guide? Thanks.


    1. Hi Kabolobari, when setting a custom post type link, WordPress “thinks” you won’t have to edit the slug anymore since you’ve set it programmatically. That’s why you aren’t able to edit it anymore.

      A plugin such as “Custom Permalinks” (https://wordpress.org/plugins/custom-permalinks/) could enable the editing once more. Feel free to look at that code if you don’t want another plugin installed and see how they enable that.


  6. Hey there! OMG thanks for this tuto. I have been searching the web for 2 days!!! I have one quick question (sorry I suck at PHP). I have 2 CPT and I would like to rewrite the URL for both. My website is currently on my local server but here is what I did.

    I used your method for the first CPT (Course_cpt) and its taxo (course_topic).
    Now, I also have a CPT called Podcast_cpt and its taxo (podcast_topic).

    I figured I would just copy and paste the code and change a few thing. The First CPT works well. For the 2nd one, I URL is created as expected but when I click on an article URL I land on a 404 page. I have reset the permalink, clear cache but it seems like it is not working so I suspect I did something wrong on my PHP.

    Here is the code:


    Can you please help?

    Thanks a lot!


    1. Hi Nad, I have pasted your code on a gist to save on the scrolling space here:)

      I have realized that you’re using a filter generate_rewrite_rules_podcast instead of generate_rewrite_rules. Fixing that might help.


  7. Hey Igor,

    I finally got it to work.

    1) I fixed the generate_rewrite_rules as you suggested.
    2) I also changed the second function to podcast_change_link

    It worked!

    Thanks a lot 😀


  8. Hi there,
    thanks for this idea, there’s one issue remaining though with this code, you will be able to link to the post with any taxonomy slug…
    If i have for taxonomy:
    e-books, e-pubs
    I will have two links available (whatever I picked as a taxonomy for my article):
    Will let you know if I find a solution for this,


  9. Thank you for the script. I have it almost working. My problem is that some of my custom posts “resources” have more than one resource_tag taxonomy on them but the link only shows one of them so the URL looks like this

    instead of this

    Is there any way to get all of the resource tags in there?


    1. Hi Dan, I have not tried it. It might require some coding for that because. If you want to include all tags to be used for a title, you would need a custom rewrite system that could handle that. At least, that’s what I think it would need, since I have not tried it yet.

      Try looking at how some other plugins are doing similar things such as “Custom Permalinks”. You can assign a different URL to a page or post and have it used like that. These could give you an insight on how to do that.


  10. I got it working using what you did and hacked away at that. Here is what I have so far but I am still working it out. I did not change the resources_type to resources in the taxonomy like you did. And kept my resources post type resources instead of the way you have it. It still needs some work so any tips would be great.


    function custom_rewrite_basic() {
    global $wp;

    $post_type = ‘resources’;

    add_rewrite_rule(‘resources/.+/(.+)$’,’index.php?post_type=’.$post_type.’&name=$matches[1]’, ‘top’);

    add_action(‘init’, ‘custom_rewrite_basic’);


    function change_link( $permalink, $post ) {

    if( $post->post_type == ‘resources’ ) {
    $resource_terms = get_the_terms( $post, ‘resource_tags’ );
    $tags = ”;
    if( ! empty( $resource_terms ) ) {
    foreach ( $resource_terms as $term ) {

    if (!$tags) {
    $tags = $term->slug;
    else {
    $tags = $tags.’/’.$term->slug;

    $permalink = get_home_url() .”/resources/” . $tags . ‘/’ . $post->post_name;
    return $permalink;


  11. Hello, thank you for code. Your code is working fine.

    What will be code if i want URL of post like this:


    Means i don’t want e-books in the URL.

    Please help.


Leave a reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.