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.

Conclusion

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.

Become a Sponsor

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.

58 Comments

  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.

    Thanks.

    Reply

    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

      Reply

  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.

    Reply

    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?

      Reply

    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.:)

      Reply

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

        Reply

        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

          Reply

  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.

    https://gist.github.com/igorbenic/3ba41de1db69bf028651e98b4615cfee#file-request2-php

    Thanks

    Reply

    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.

      Reply

  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.

    Thanks

    Reply

    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!

      Reply

      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:)

        Reply

        1. Hello! Thank you for the article. I can not figure out one problem. Hello! Thank you for the article. I can not figure out one problem. How to redirect and write rule for archive page CPT. That is, as the commentator did Gareth.
          Igor Benic can you tell me how to do it right? Thank you very much!

          Reply

          1. Hi Dima, I would look into create custom rewrite rules for custom archive url pages such as in the accepted answer of this stackoverflow question: https://wordpress.stackexchange.com/questions/229665/custom-rewrite-rules-for-archive-page-and-single-post

        2. It is a pity that you do not respond to comments.

          Reply

          1. I hope you understand that I am doing this in my own free time which I do not have much.

      2. Hi Garet, It would be great if you can share your solution on GitHub.

        Best!

        Reply

  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.

    Reply

    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.

      Reply

  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:

    https://gist.github.com/igorbenic/07912f87e9761bd66e0bd8a757a12c1c

    Can you please help?

    Thanks a lot!

    Reply

    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.

      Reply

  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 😀

    Reply

  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):
    resources/e-books/my-new-book
    resources/e-pubs/my-new-book
    Will let you know if I find a solution for this,
    Best,
    David

    Reply

  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

    site.com/resources/resource_tag/title
    instead of this
    site.com/resources/resource_tag/resource_tag//title

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

    Reply

    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.

      Reply

  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.

    // CUSTOM URL WITH TAXONOMIES FOR RESOURCES

    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’);

    // CREATE CUSTOM PERMALINK FOR RESOURCES THAT INCLUDES TAGS

    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;
    }
    add_filter(‘post_type_link’,”change_link”,10,2);

    Reply

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

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

    resources_post_type/my-new-book

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

    Please help.

    Reply

    1. Hi Jai, then you would not need the last part of this code where we create a custom post type link.

      Reply

  12. HI. thanks for sharing, took me few days before i find your site .

    I beleive this code dont work with multi level taxonomies ?

    Reply

    1. Hi Dee,

      You would need to refactor the code to get parent and child terms from the parent. Then use the parent slug to form the URL with the child slug.

      Reply

  13. Giang Lê Hoàng January 2, 2019 at 2:13 pm

    I use Custom taxonomy = ABC which have term like “a1, b1, c1…”. Now I want my posts’ permalinks structure is like this: abc.com/a1/post-1 and abc.com/c1/post-2.

    I set Custom Structure in Permalink = /%ABC%/%postname% but it did not work.

    Can you guide me a proper way to achieve my need?

    Many thanks!

    Reply

    1. Hi Giang, setting a custom permalink structure won’t help you here because your need is very specific and it’s specific to each post uniquely.

      You would require someone to programmatically create via a plugin or a code snippet in the current theme you’re using (I would recommend it having it as a separate plugin so it remains there if the theme is changed)

      Reply

  14. Don’t know why anyone else hasn’t found this error

    snt_resource_type should be resource_type, or vice versa. Otherwise it won’t work

    Reply

    1. Fixed it. Sorry about that 🙂 Thank you for reporting it.

      Reply

  15. I add this all code and add new post into new post type.

    URL is showing good but when i try to click on this url this will not go to my post and redirect to home page.

    Can you please help on this

    Thank you

    Reply

    1. Hi Imran, did you reset the rewrite rules? It might even be an issue with the code if you’ve only copied it.

      Reply

  16. samuel hadsall June 13, 2019 at 8:38 pm

    Thanks for the article, I am using this approach on my custom post type. However, I’m only able to get the rewrite working on one of my taxonomies. I have a series_type and a series taxonomy created and set up both with rewrite rules. My series_type is getting redirected to home page where as my series is working as expected. Any ideas? I can send you code if need be.

    Reply

  17. Hi Igor!

    Thanks for this code. It has been very useful.
    I’ve tried to append another custom taxonomy to the url so it has this form mywebsite.com/custom-taxonomy1/custom-taxonomy2/postname

    I haven’t had luck modifying your code to achieve that.

    I have read in your comments this: That is why even core WordPress won’t give you the ability to have category-1/category-2

    So I think this structure is really hard to get working isn’t it?

    Thanks!

    Reply

    1. Hi Daniel, yes, having two taxonomies together is a hard thing to achieve. Not sure if it is even possible with WordPress.

      Reply

      1. Thanks for your reply Igor.

        As I told you I wasn’t able to get it working so I finally ended using just one taxonomy.

        Reply

  18. Samuel Hadsall June 17, 2019 at 11:10 pm

    Hi Igor, I used your code here as a starting point for my custom post type rewrites. I two taxonomies for my post type “series type and series”. However, it looks like WordPress is thinking that my series type is my series, so when trying to navigate to a series type archive it redirects me to the home page. Would you have any guidance you could give me on? I can send my code if need be.

    Reply

    1. Hi Samuel, if you have two taxonomies and try to combine them together, I am afraid this will be a hard thing to do. I am not sure if it is even possible. You might want to think about using the same taxonomy where the parent would be a series type, while the children of each series type would be the series?

      Reply

  19. Thank you for sharing this tutorial.

    I need to rewrite URL

    mydomain.com/film

    to rewrite

    mydomain.com/best/film

    and also

    mydomain.com/movies/film

    to rewrite

    mydomain.com/best/movies

    I am trying to do this but having an error

    function prefix_WP_rewrite_rule() {
        add_rewrite_rule( ‘best/([^/]+)/film’, ‘index.php?WP=$matches[1]&photos=yes’, ‘top’ );
        add_rewrite_rule( ‘film/([^/]+)/movies’, ‘index.php?WP=$matches[1]&videos=yes’, ‘top’ );
    }
     
    add_action( ‘init’, ‘prefix_WP_rewrite_rule’ );

    I have seen the code in this tutorial and implemented it step by step.

    Reply

    1. Hi Amanda, what is WP inside of the rewrite rule?

      Also, if you want to have best/movies as a URL, and the ‘movies’ is a fixed category ,the rewrite rule would then be add_rewrite_rule( ‘best/movies’, ‘index.php?CATEGORY_TYPE=movies’, ‘top’ ); where CATEGORY_TYPE would be the taxonomy and the ‘movies’ would be the slug of a term Movies.

      Your rule is formed as if you are expecting something between best/SOMETHING_HERE/movies.

      Reply

      1. Can you please guide me how the rewrite rule implemented here in this tutorial https://www.wpblog.com/wordpress-url-rewrite-for-custom-page-template/ as have done all the steps thhat mentioned here and getting error.

        Reply

  20. This is my site : https://www.texasrealfood.com/

    CPT : listing
    Taxonomy : location
    Taxonomy : listing-category

    I want url like this one

    https://www.texasrealfood.com/location-name/listing-category-name

    Reply

  21. Why do you generate a rewrite rule for each individual term? My example with ‘project’ and ‘project category’ (project_cat):

    $rules[‘project/([^/]*)/([^/]+)/?$’] = ‘index.php?post_type=project&project_cat=$matches[1]&name=$matches[2]’

    If you want hierarchical category use ‘(.+?)’ for the first match, like ‘project/(.+?)/([^/]+)/?$’; Using this requires adjusting ‘post_type_link’ as well.

    Reply

    1. Hi, that could probably work as well. I am not sure if I tried that but it did not work on my own setup so I went with a more specific approach.

      But your example should also work and it might be a much better solution as well since it required less resources to compute.

      Reply

  22. I haven’t had luck modifying your code to achieve that.

    I have read in your comments this: That is why even core WordPress won’t give you the ability to have category-1/category-2

    Reply

    1. Hi, that’s correct. Getting two categories of separate taxonomies is a complex thing and I have not yet had the opportunity to really work with it to try and hack around the code.

      WordPress has a category/sub-category but that’s of the same taxonomy.

      Reply

  23. Hi Igor,

    Your code works well, thank you for sharing it. I’m curious, how would you modify this to account for sub categories? I have a number of sub-categories on my resource post type that I would like to account for:

    resources/design/sub-category/post-name

    resources/design/graphic-design/post-name
    resources/design/illustration/post-name

    Is this possible and furthermore, possible to create a “page” that lists each post item for its parent and sub category?

    Thanks

    Reply

    1. Hello Roger,

      Hi Roger, have you found a solution for the subcategories?

      Reply

  24. Hi Igor,

    Your code works well, thank you for sharing it.

    Reply

  25. Great tutorial – I have one minor issue, which I think might be to do with the post status ets.

    When I try to move the post to the trash, it simply sets the post to draft (if I do it from on the edit post and ‘Move to bin’).

    Equally, if you use the quick edit on the listing screen to edit a post that is already published, it reverts to draft status.

    Any thoughts on how we can overcome this?

    Many thanks
    Cam

    Reply

  26. Any solution for subcategories?

    resources/design/graphic-design/post-name1
    resources/design/illustration/post-name2
    resources/design/post-name3

    There must be some solution for this very common case

    Reply

  27. Hi everyone, I finally found the solution to the problem of hierarchical categories for custom post types.

    With the permalink manager pro plugin it is possible

    https://permalinkmanager.pro/docs/tutorials/add-category-slug-wordpress-permalinks/

    After talking to the plugin developer, you need to apply a filter to your functions.

    add_filter (‘permalink_manager_primary_term’, ‘__return_false’);

    : )

    Reply

  28. Hello Igor,

    I appreciate the valuable information you provided. It has been very helpful and effective. Currently, I’m using the Pods plugin to generate custom post types. However, I’m encountering an issue where the plugin doesn’t switch to another language when using WPML.

    To resolve this issue, I have already taken some measures. For instance, I have set the custom post type to be translatable using the Translation Management plugin. Additionally, I have reviewed the settings for the WPML Language Switcher and made sure they are configured correctly. Unfortunately, despite my efforts, the language switcher still doesn’t work properly for my custom post-type entries.

    After conducting various tests, I have discovered that the language switcher works without any problems until I apply the code. Once I apply the code, I cannot switch languages anymore. Do you think you could assist me in resolving this issue and improving my code?

    Please let me know if you require any further information from my end.

    Thank you for your assistance.

    Best regards,
    Ledia

    Reply

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.