I've been beating my head against the wall for a few days trying to figure this out. I have a custom post type and custom taxonomy and I'm trying to get my URL's to work.

Here is the URL structure that I am going for: http://url.dev/custom-post-type/taxonomy/post-name/

The first two parts are working. I can go to just custom-post-type and it uses my archive-custom-post-type.php file. I can also go to /custom-post-type/taxonomy and it uses my taxonomy-name.php.

However, when I try to go to an actual post, it gives me a 404 error page. I am using the Rewrite Analyzer plugin, and it looks like my rewrite rules are correct. Here are a few screenshots.

enter image description here

enter image description here

That looks right, that's what needs to happen. But I get a 404 error when I try to go to one of the single products page.

Here is my actual rewrite rule in my functions.php

function taxonomy_slug_rewrite($wp_rewrite) {
    $rules = array();
    // get all custom taxonomies
    $taxonomies = get_taxonomies(array('_builtin' => false), 'objects');
    // get all custom post types
    $post_types = get_post_types(array('public' => true, '_builtin' => false), 'objects');

    foreach ($post_types as $post_type) {
        foreach ($taxonomies as $taxonomy) {

            // go through all post types which this taxonomy is assigned to
            foreach ($taxonomy->object_type as $object_type) {

                // check if taxonomy is registered for this custom type
                if ($object_type == $post_type->rewrite['slug']) {

                    // get category objects
                    $terms = get_categories(array('type' => $object_type, 'taxonomy' => $taxonomy->name, 'hide_empty' => 0));

                    // make rules
                    foreach ($terms as $term) {
                        $rules[$object_type . '/' . $term->slug . '/([^/]*)/?'] = 'index.php?pagename=$matches[1]&' . $term->taxonomy . '=' . $term->slug;
    // merge with global rules
    $wp_rewrite->rules = $rules + $wp_rewrite->rules;
add_filter('generate_rewrite_rules', 'taxonomy_slug_rewrite');

Is there anything I'm doing wrong?

If I turn the permalinks back to the default, I can go to: http://uss.local/?ultrasound-equipment=sequoia-512&equipment-manufacturer=acuson and it works just fine. But when I switch over to "Post Name" permalink it doesn't work.

  • pagename is the query var for the page post type.
    – Milo
    Commented May 14, 2014 at 1:10
  • You're right.. It should be '.$post_type->slug.'='.$matches[2].' instead of pagename. Thank you!
    – Drew
    Commented May 14, 2014 at 1:16

1 Answer 1


You can achieve this without adding a rule for every term via the rewrite argument when you register your post type and taxonomy. The only extra rule you need is to handle pagination for the taxonomy.

First, register the post type. The order you register post type and taxonomy is important! I've left out most arguments to focus on the important bits:

$args = array(
    'has_archive' => 'custom-type',
    'rewrite' => array(
        'slug' => 'custom-type/%custom-tax%',
        'with_front' => false
register_post_type( 'custom-type', $args );

Then register the taxonomy:

$args = array(
    'rewrite' => array(
        'slug' => 'custom-type',
        'with_front' => false
register_taxonomy( 'custom-tax', array( 'custom-type' ), $args );

Then add the rewrite rule to handle taxonomy pagination:

function wpd_taxonomy_pagination_rewrites(){
add_action( 'init', 'wpd_taxonomy_pagination_rewrites' );

And to get the selected term in the permalink, filter post_type_link:

function wpd_post_link( $post_link, $id = 0 ){
    $post = get_post($id);  
    if ( is_object( $post ) && $post->post_type == 'custom-type' ){
        $terms = wp_get_object_terms( $post->ID, 'custom-tax' );
        if( $terms ){
            foreach( $terms as $term ){
                if( 0 == $term->parent ){
                    return str_replace( '%custom-tax%' , $term->slug , $post_link );
        } else {
            return str_replace( '%custom-tax%' , 'uncategorized', $post_link );
    return $post_link;
add_filter( 'post_type_link', 'wpd_post_link', 1, 3 );
  • 8 years later and this is still working great !! Tried so many options, all broken! Commented Oct 31, 2022 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.