I have two custom post types:

  • Events
  • Businesses

I have a new taxonomy that both these CTP's share:

  • Locations

If I go to "domain.com/events/" I see an archive of all the events, and if I go to "domain.com/businesses/" I see an archive of all the businesses. So far, so good.

If I go to "domain.com/locations/" I see an archive of all the events AND businesses, which is Ok.

However, how do I view the event locations only? "domain.com/locations/events" or "domain.com/events/locations" doesn't work.

Do I have to change the rewrite settings for either the locations taxonomy or the CPT? It seems I'm stuck!

  • Permalinks are current set to just /%postname%/ by the way.
    – pixelkicks
    Commented Sep 9, 2013 at 10:50
  • "If I go to 'domain.com/locations/' I see an archive of all the events AND businesses, which is Ok." Have you added a custom rewrite rule for that? Normally WP with this url look for a page with the slug 'locations'. Maybe do you intend 'domain.com/locations/a-location'?
    – gmazzap
    Commented Sep 11, 2013 at 3:50
  • No, haven't added anything custom. The taxonomy has been defined for BOTH the post types though, so wouldn't this be normal function?
    – pixelkicks
    Commented Sep 11, 2013 at 7:43
  • @Buckers @g-m it is a normal function I think, it would happen if when generating the rewrite rules for the taxonomy if the walk_dirs parameter was true. The rule would return posts with any location term assigned. Commented Feb 24, 2014 at 11:12

2 Answers 2


You'll have to add some new rewrite rules, thankfully WordPress makes it fairly straightforward using a few function calls.

// use the init action to modify rewrite rules
add_action( 'init', function() {
    global $wp_rewrite;

    // add rewrite tag for the post type
    // - %posttype% is a new tag that will be replaced with the regex in the actual query
    // - the regex will extract the post_type name from the URL and use it in the query
    add_rewrite_tag( '%posttype%', '([^/]+)', 'post_type=' );

    // create the new permastruct by combining the post type & taxonomy permastructs
    // - custom taxonomies and post types are added to the extra_permastructs array in WP_Rewrite
    // - change 'category' to desired taxonomy
    // - output will be '%posttype%/category/%category%'
    $types_and_cats_permastruct = '%posttype%' . $wp_rewrite->get_extra_permastruct( 'category' );

    // add the permastruct for post type & taxonomy
    add_permastruct( 'post_type_and_category', $types_and_cats_permastruct, array(
        'with_front' => true,           //  - with_front (bool) - Should the structure be prepended with WP_Rewrite::$front? Default is true.
        'ep_mask' => EP_ALL_ARCHIVES,   //  - ep_mask (int) - Endpoint mask defining what endpoints are added to the structure. Default is EP_NONE.
        'paged' => true,                //  - paged (bool) - Should archive pagination rules be added for the structure? Default is true.
        'feed' => true,                 //  - feed (bool) - Should feed rewrite rules be added for the structure? Default is true.
        'forcomments' => false,         //  - forcomments (bool) - Should the feed rules be a query for a comments feed? Default is false.
        'walk_dirs' => false,           //  - walk_dirs (bool) - Should the 'directories' making up the structure be walked over and rewrite
                                        //    rules built for each in turn? Default is true.
        'endpoints' => true             //  - endpoints (bool) - Should endpoints be applied to the generated rewrite rules? Default is true.
    ) );

} );

So to explain what I'm doing above:

  1. Adding a rewrite tag with add_rewrite_tag() so that when you put the custom post type name into the URL (eg. /events/location/uk/) it will be recognised by the regular expression and the resulting query WP runs would contain post_type=events
  2. Making a new permastruct to generate rules for using the tag we just added and the existing taxonomy permastruct, in your case this would be location so /event/location/uk will show uk events and /business/location/uk will show uk businesses
  3. Adding the new permastruct with add_permastruct() so that WP generates the new rewrite rules whenever permalinks are refreshed. In the 3rd parameter it's very important to have walk_dirs set to false as you'll get some undesirable aggressive rewrite rules with archives for /%posttype%/category/ and /%posttype%/


  • Generating links - if you want to use the_terms() to output location links for a business or event that only shows the originating post type you'll have to filter the URLs or write a custom function to output URLs to match your permastruct
  • If you never want an archive listing both events and businesses then 2 separate taxonomies is a better idea as you'll more easily be able to tell if a term has any posts in it eg. when generating links and deciding whether to output a link to an empty archive or not. In that case @richard-howarth's answer is correct

You can specify the slug to use in the args when calling the register_taxonomy function by using 'rewrite'.

So, you'd have something like the following:

$args = array(
    'labels' => $labels,
    'rewrite' => array( 'slug'=>'events/locations' )
register_taxonomy( 'event_location_tax_name', 'event_location_cpt_name', $args );

And then replicate for Businesses :

$args = array(
    'labels' => $labels,
    'rewrite' => array( 'slug'=>'businesses/locations' )
register_taxonomy( 'business_location_tax_name', 'business_location_cpt_name', $args );

Hope that helps?!

Thanks Rik

  • 1
    Wouldn't that create two separate taxonomies though? I would like to share the same taxonomy, as all the terms would be the same.
    – pixelkicks
    Commented Sep 10, 2013 at 22:46
  • Can you do something like 'rewrite'=>array('slug'=>'%post_type%/locations') and then pass the CPTs as an array into the register_taxonomy : array( 'event_location_cpt_name', 'business_location_cpt_name')? I haven't actually tried myself (so could be rubbish!), but it might depend on how WP handles %post_type% Commented Sep 11, 2013 at 19:05

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.