WordPress permalink and redirect made simple

If you are using WordPress inside a company, will come the time to change the permalink structure of your custom posts type (CPT), as request by the SEO department. I said company, because almost nobody will invest so much money for such a thing.

In my case, the request has been to create a structure like /custom_taxonomy/custom_taxonomy_nth_child/custom_post which it’s also reasonable, but can create a lot of problems if you are not aware of how slugs works.

As you should probably know, such standard structure it’s not the default and it’s achievable, without change code, just with posts. Whenever you do not know, you can do that through your Permalinks setting, writing %category%/%postname% inside “Custom Structure”. You can find more information inside the WP codex.

This last hint will help you to understand how can you extend it for your CPT. The whole process is basically done in 2 steps: the first to restructure your permalinks, the second one to make WordPress recognize them.


Step 1: Rewrite tag

Assuming your CPT name or slug is  custom_post , you need to define the tag just like the post and the category above, using %custom_post%.

This will allow you to play around with it as follow:

add_action('init', 'custom_post_rewrite');
function custom_post_rewrite($rewrite) {

  global $wp_rewrite;

  add_rewrite_tag('%custom_post%', '([^/]+)', 'custom_post=');
  add_permastruct('custom_post', '/%category%/%custom_post%', false);


In this way we modify then the  permalink structure (add_permastruct) prefixing with the standard /%category%/ , though it will still not have the whole tree.

The reason we rewrite the rule, it’s because the ugly and original version of the permalink of your CPT would be index.php?custom_post= . You can verify this playing with the settings.

Also remember, you will need to update the permalinks every time you make a modification. To avoid this hassle while developing, you can add at the bottom the line $wp_rewrite->flush_rules() , keeping in mind to remove it at final deployment, since this will trigger at every post submission.


Step 2: Permalinks

Now we have finally defined our tag and made the CPT prefixed automatically by WP by the category. However we still do not have the whole hierarchy, so we’ll need to update again the permalink structurelike this:

function custom_post_permalinks( $permalink, $post ) {
  if ( $post->post_type == 'custom_post' ) {
    if ( $cats = get_the_terms( $post->ID, 'category' ) ) {
        $cat_current = current( $cats );
        $cats_parent = get_category_parents( $cat_current, false, '/', true );
        $cats_parent_fix =  substr($cats_parent, 0, strlen($cats_parent) -1 );
      $permalink = str_replace( '%category%', $cats_parent_fix, $permalink );
    } else {
        return $permalink;
  return $permalink;
add_filter( 'post_type_link', 'custom_post_permalinks', 10, 2 );

In few words, we check if the post is our CPT and has categories, to retrieve the whole tree using / as divider and then replace the whole tag with the whole tree. I’ve added $cats_parent_fix to avoid repetition of our CPT.


An article has been posted from Yoast SEO about the permalink structures, which clarifies the technical reasons of the choice.