Rewrite Jetpack’s Custom Taxonomies

On one of my WordPress-powered sites, I use a blog post URL prefix, or ‘front’. Essentially, all blog post URLs look like, and category and tag URLs are prefixed by blog, too.

I wanted to also start using Jetpack’s Portfolio custom post type, though, and not have URLs for its custom ‘Project Type’ and ‘Project Tag’ taxonomies prefixed by the same front. A portfolio prefix, I figured, would definitely make more sense!

So, I went ahead and tweaked the custom taxonomy definition:

function edit_project_taxonomies() {
  $project_type = get_taxonomy( 'jetpack-portfolio-type' );

  $project_type->rewrite['slug'] = 'portfolio/type';
  $project_type->rewrite['with_front'] = false;

  register_taxonomy( 'jetpack-portfolio-type', 'jetpack-portfolio', (array) $project_type );

  $project_tag = get_taxonomy( 'jetpack-portfolio-tag' );

  $project_tag->rewrite['slug'] = 'portfolio/tag';
  $project_tag->rewrite['with_front'] = false;

  register_taxonomy( 'jetpack-portfolio-tag', 'jetpack-portfolio', (array) $project_tag );
add_action( 'init', 'edit_project_taxonomies', 11 );

Note that you can very simply ‘re-register’ existing custom taxonomies, provided you first fetch all of their arguments. And two more things:

  • The 'with_front' = false argument is what actually disables having my blog front added to the portfolio type and tag URLs.
  • It’s perfectly fine to use a forward slash in a taxonomy slug. (Still, if you find this leads to 404 or other errors: please read on!)

Also, normally this would suffice. In this case, however, the URL rewrite rules generated by the code above are preceded by a more general rule set—itself an effect of the ‘jetpack-portfolio’ custom post type using the same ‘portfolio’ in its automatically assigned URLs. At this point, WordPress will misinterpret any request for a portfolio taxonomy archive and return a 404 instead!

Luckily, we can bump our bespoke taxonomy rules’ priority up a bit, so that they become leading:

function modify_project_taxonomies( $wp_rewrite ) {
  $rules = array(
    'portfolio/type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$' => 'index.php?jetpack-portfolio-type=$matches[1]&feed=$matches[2]',
    'portfolio/type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$'      => 'index.php?jetpack-portfolio-type=$matches[1]&feed=$matches[2]',
    'portfolio/type/([^/]+)/embed/?$'                         => 'index.php?jetpack-portfolio-type=$matches[1]&embed=true',
    'portfolio/type/([^/]+)/page/?([0-9]{1,})/?$'             => 'index.php?jetpack-portfolio-type=$matches[1]&paged=$matches[2]',
    'portfolio/type/([^/]+)/?$'                               => 'index.php?jetpack-portfolio-type=$matches[1]',
    'portfolio/tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$'  => 'index.php?jetpack-portfolio-tag=$matches[1]&feed=$matches[2]',
    'portfolio/tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$'       => 'index.php?jetpack-portfolio-tag=$matches[1]&feed=$matches[2]',
    'portfolio/tag/([^/]+)/embed/?$'                          => 'index.php?jetpack-portfolio-tag=$matches[1]&embed=true',
    'portfolio/tag/([^/]+)/page/?([0-9]{1,})/?$'              => 'index.php?jetpack-portfolio-tag=$matches[1]&paged=$matches[2]',
    'portfolio/tag/([^/]+)/?$'                                => 'index.php?jetpack-portfolio-tag=$matches[1]',

  $wp_rewrite->rules = $rules + $wp_rewrite->rules;
add_filter( 'generate_rewrite_rules', 'modify_project_taxonomies' );

This (finally!) results in the proper routing scheme:

  • /portfolio/: overall portfolio archive
  • /portfolio/project-name/: single project page
  • /portfolio/type/some-type/: project archive for ‘some type’
  • /portfolio/tag/some-tag/: project archive for ‘some tag’

One final remark: I much prefer manually defining my application’s routes over WordPress’ automatic definition of literally all sorts of routes.