Custom permalink structure for custom post types

Hey guys, Jimbo here. So today I wanted to talk about custom permalink structures in WordPress. Specifically custom permalinks structures for custom post types (CPT). I’ve done some googling on the subject and I think the general internet don’t have so much info on this topic.

So, you have created a custom post types in WordPress, and now you want a specific permalink structure for that post type. This seems to be especially true for people that want their taxonomy terms to show in their permalinks. While this is definitely possible, it’s not really optimal since a post can always have multiple terms by default, hence not ideal for a permalink structure.

If you want a basic parent/child permalink structure you might be better of using the parent child functionality for your custom post type. The problem with this approach is that it gets kind of clunky to administrate if you have a lot of content. The upside is that WordPress automatically resolves the permalinks (if you have pretty permalinks enabled) to parent/child and so on for as man children and grandchildren you might have. If you know any plugins to improve the usability of the archive list in the WordPress-admin for complicated ancestor-structures please comment down below.

Enough of the lull lull, let’s create a permastruct for a CPT. I’m not completely sure what hook you SHOULD use to do this but you CAN use the init-action. I guess it would be possible to do it on the after_setup_theme-hook as well, though I don’t think I’ve tested this. Anyway, the last time I did it I hooked it to “init” and it worked. So, let’s say we have a custom post type registered as “jimbos_post_type” and we want to try and make use of a custom taxonomy in our permalink structure like so: “/jimbos-corner/custom-taxonomy/jimbos_post_type”. It would look something like this (put in functions.php):

// We hook our function to the init-action
add_action( 'init_rewrites', 'init' );

function init_rewrites() {
  // We will work with the global wp_rewrite object
  global $wp_rewrite;

  // Create your structure
  // jimbos-corner is our base slug and will be the same for every post
  // %taxonomy_term% is surrounded with percentage to easily find it and
  // replace it later on.
  // The last part of the url should be the registered name of your CPT,
  // that means WordPress automatically resolves this part to the post slug
  // and it will be editable on the post edit page
  // dont forget to surround with %
  $structure = '/jimbos-corner/%taxonomy_term%/%jimbo_post_type%';

  // For our %taxonomy_name% to fully function we must add it as a tag,
  // I'm not totally sure why this step is necessary but I cannot get it
  // work without it
  $wp_rewrite->add_rewrite_tag("%taxonomy_term%", '([^/]+)', "taxonomy_name=");

  // Lastly we add our permastruct and connect it to our CPT
  // First argument should be the registered post type name
  $wp_rewrite->add_permastruct('jimbos_post_type', $structure, false);

If you now go to your wordpress-admin and one of your custom post type called “Test” you should see it has the following permalink:


Kinda awesome huh?

So the next step is to switch out the %taxonomy_term% part of our permalink into something that actually makes sense. To do this we will hook into a filter that fires just before permalinks are being outputted called post_type_link

add_filter( 'post_type_link', 'setup_permalinks', 10, 3 );
// When hooking to post_type_link we get two aruments
// The current post being processed and its current permalink
function setup_permalinks($permalink, $post) {
  // Remember, all posts on your installation will pass this function
  // But we only want to intercept those belonging to jimbos_post_type
  if ( get_post_type($post) === 'jimbos_post_type' ) {
    // Get all terms checked on current post
    // custom_taxonomy is the name of some custom taxonomy
    $post_terms = wp_get_post_terms($post->ID, 'custom_taxonomy');

    // If there is only one term ticked for this post, we will use its slug
    if ( count($post_terms) === 1 ) {
      $taxonomy_term = $post_terms[0]->slug;
    // If there is more than one, or none, we have a hardcoded fallback
    } else {
      $taxonomy_term = 'whatever';

    // Make sure the permalink have %taxonomy_term% in it
    if ( strpos($permalink, '%taxonomy_term%') !== false ) {
      // Replace %taxonomy_term% to whatever reside in the $taxonomy_term variable
      $permalink = str_replace('%taxonomy_term%', $taxonomy_term, $permalink);

  return $permalink

As you can see above, this is where it gets problematic to use taxonomy terms in your permalinks, since there really is no good way of handling multiple terms. However this technique is applicable to other scenarios and it can really come in handy to have this knowledge in your arsenal.

If you now revisit your custom post named test, you should see that the %taxonomy_name% part is replaced by a term-slug or “whatever”. Although the actual link will not work since we have to flush the permalinks in order for WordPress to update its rewrite rules.

This can be done by simply going to Settings->Permalinks and click the blue Save button.

And that’s it, you should now have a custom permalink structure for you custom post type.

Something to note is that the WordPress codex on this topic is not really that informative as one would like but to give some more sources of reading, here are some links:

WP_Rewrite class

When should add_rewrite_tag() be used?

Tutorial that helped me do this the first time