I have a custom post type and two custom taxonomies associated with it. One is locations and the other is activities. How can I form the permalinks in the following format?


Therefore the first one would be the location taxonomy and second would be the activity taxonomy?

  • if the url can be something like http://mysite.com/usa/activity/diving it will be a lot simpler...
    – gmazzap
    Commented Aug 21, 2013 at 17:35
  • how will WordPress know you want taxonomy terms instead of a page named usa with a subpage named diving?
    – Milo
    Commented Aug 21, 2013 at 17:42
  • Only way is get all terms for both taxonomies on every request, compare them with the url parts and if they match do the work. But this: 1) will slow down the site a lot 2) in case of collisions (one pointed out my @milo) the 'default' request never go successfull.
    – gmazzap
    Commented Aug 21, 2013 at 17:53
  • I think I've been trying to go down a close ended road. Probably easier to create a page with a custom query that fetches what I want. Thanks for clarifying.
    – urok93
    Commented Aug 22, 2013 at 21:47
  • Take a look here: wordpress.stackexchange.com/questions/11582/…; and here: wordpress.stackexchange.com/questions/5308/… - good luck! Commented Aug 23, 2013 at 9:57

4 Answers 4


I recently talked about this at a couple of WordCamps. Here's my talk from WC Portland, and the slides, source code, etc. Check out Recipe #2 specifically.

First off, you should probably have a static prefix to start the URLs. It's not 100% necessary, but without it your rewrite will conflict with page permalinks. If you decide to use the static prefix, you might want to make it about the post type, since that's what your taxonomies organize. I don't know what that is, so I'm making a guess that it's about "guides." In that case, your URIs would be something like /guides/usa/diving/.

Here's some code to get you started. If anything doesn't make sense to you, you should watch the presentation and follow along with the slides.


 * Setup custom permalinks with multiple taxonomies

if ( !class_exists( 'WPSE_110508' ) ) :

class WPSE_110508 {

    private static $instance;

    public static function instance() {
        if ( ! isset( self::$instance ) ) {
            self::$instance = new WPSE_110508;
        return self::$instance;

    public function setup() {
        add_action( 'init', array( $this, 'structures' ) );
        add_filter( 'post_type_link', array( $this, 'post_type_link' ), 10, 2 );
        add_filter( 'term_link', array( $this, 'term_link' ), 10, 3 );

     * Register our data structures
     * @return void
    function structures() {
        register_taxonomy( 'location', 'guide', array(
            'label' => 'Body Types',
            'rewrite' => array( 'slug' => 'guides' )
        ) );

        add_permastruct( "all_activities", "guides/all/%activity%" );
        register_taxonomy( 'activity', 'guide', array(
            'label' => 'Activities',
            'rewrite' => array( 'slug' => 'guides/%location%' )
        ) );

        register_post_type( 'guide', array(
            'public' => true,
            'label' => 'Guides',
            'rewrite' => array( 'slug' => 'guides/%location%/%activity%' )
        ) );

     * Filter post type links for guides to replace %location% & %activity% if present
     * @param string $link
     * @param object $post
     * @return string
    function post_type_link( $link, $post ) {
        if ( 'guide' == $post->post_type ) {
            if ( $locations = get_the_terms( $post->ID, 'location' ) ) {
                $link = str_replace( '%location%', array_pop( $locations )->slug, $link );
            if ( $activities = get_the_terms( $post->ID, 'activity' ) ) {
                $link = str_replace( '%activity%', array_pop( $activities )->slug, $link );
        return $link;

     * Filter term links for activities to replace %location% with "all"
     * @param string $termlink
     * @param object $term
     * @param string $taxonomy
     * @return string
    function term_link( $termlink, $term, $taxonomy ) {
        if ( 'activity' == $taxonomy ) {
            return str_replace( '%location%', 'all', $termlink );
        return $termlink;


function WPSE_110508() {
    return WPSE_110508::instance();
add_action( 'after_setup_theme', 'WPSE_110508' );


It seems you're using two non-hierarchical custom taxonomies. Try using a single hierarchical custom taxonomy (imagine how categories work). That way your URLs would be able to look like "usa/diving".

When you register your custom taxonomy via your theme's functions.php, do something like this:

'hierarchical' => true,

The .php function is this <?php get_term_link( $term, $taxonomy ); ?> and here you have an example of how you can use this into your code: $terms = get_terms('species');

echo '<ul>';
foreach ($terms as $term) {
    //Always check if it's an error before continuing. get_term_link() can be finicky sometimes
    $term_link = get_term_link( $term, 'species' );
    if( is_wp_error( $term_link ) )
    //We successfully got a link. Print it out.
    echo '<li><a href="' . $term_link . '">' . $term->name . '</a></li>';
echo '</ul>';

It seems as though you actually have multiple options here.

Good luck!

  • 1
    I would also recommend the first one, Custom Permalinks.
    – Ciprian
    Commented Aug 26, 2013 at 13:56

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.