custom taxonomy and pages rewrite slug conflict gives 404

You seem to need “partial verbose rewrite rules”. Verbose rewrite rules means all the pages are put on top because WordPress can’t figure out the difference between a page and a post. Here it thinks it can, because all URLs of the form portfolio/([^/]+)/ are from your portfolio taxonomy, except this one portfolio/clients/. You will have to put that one on top of the rewrite rules, so it matches before the more generic portfolio taxonomy. You could probably also force all of the rewrite rules to be verbose, but that will impact the performance if you have lots of pages.

This answer is written with my just-gained understanding of rewrite rules, so I hope it is a good way to do it and the example code doesn’t contain too many errors.

A page does not generate just one rewrite rule, it generates a group:

  • (pagename)/trackback/?$
  • (pagename)/feed/(feed|rdf|rss|rss2|atom)/?$
  • (pagename)/(feed|rdf|rss|rss2|atom)/?$
  • (pagename)/page/?([0-9]{1,})/?$
  • (pagename)/comment-page-([0-9]{1,})/?$
  • (pagename)(/[0-9]+)?/?$

You don’t have to create these yourself, you can re-use the power of WP_Rewrite. Look at its page_rewrite_rules() method: if we are in verbose mode, it gets a list of all pages (via page_uri_index()) and their attachments, overwrites the %pagename% rewrite tag, and generates the rewrite rules for this page. We can do this too:

// We only generate them for this page
$page_uri = 'portfolio/clients';
// Returns site root + '%pagename%'
$page_structure = $wp_rewrite->get_page_permastruct();
// Everywhere you see %pagename% in the structure used to generate rules
// in the next step, replace it with our fixed page name
$wp_rewrite->add_rewrite_tag('%pagename%', "({$page_uri})", 'pagename=");
// This generates the group given above
$page_rewrite_rules = $wp_rewrite->generate_rewrite_rules($page_structure, EP_PAGES);

This will give us the rules for the pages, but not yet for attachments used in the page. If you also want them, you repeat the step for each attachment, but with add_rewrite_tag("%pagename%', "({$attachment_uri})", 'attachment=") (see page_rewrite_rules() for more details).

Good, we got the rules, but now you need to add them to the complete rewrite structure in some way. You could do this with add_rewrite_rule(), but you must call it for every rule generated in the $page_rewrite_rules array. For this reason, many people hook into the rewrite_rules_array filter, since you can just modify the array there.

add_filter("rewrite_rules_array', 'add_verbose_portfolio_clients_page');
function add_verbose_portfolio_clients_page($rewrite_rules)
{
    global $wp_rewrite;

    // The previous code snippet comes here, where we generate $page_rewrite_rules

    // Our rules have priority, they should be on top
    $rewrite_rules = array_merge($page_rewrite_rules, $rewrite_rules);

    return $rewrite_rules;
}

After you included this filter, you should flush the rewrite rules (once, not one every page load, as it is quite heavy). You can do this by calling flush_rewrite_rules(), or by visiting the “Permalinks” settings page.

Leave a Comment