I have successfully created a custom post type with a URL rewrite that changes URLs from:




This is working, but there have been some changes to my project that require me to do this instead:


Essentially, the first part of the path needs to be dynamic so that both "some-string" and "some-other-string" are valid.

I've updated the rewrite rules to accommodate this change, and those pages do work correctly, but none of the other pages on the site (except the front page) work at all. I'm seeing the header and footer, but no content.

Here's the relevant section of my custom post type registration:

'rewrite' => array(
    'slug' => '%keyterms%/%community%/%address%',
    'with_front' => false,

And here's my code:

function inventory_rewrite_tags() {
    add_rewrite_tag('%keyterms%', '(.*)');
    add_rewrite_tag('%community%', '(.*)');
    add_rewrite_tag('%address%', '(.*)');
add_action('init', 'inventory_rewrite_tags');

function inventory_link_rewrite($link, $post) {
    if($post->post_type === 'inventory') {
        $community = get_field('community', $post->ID);
        $address = get_field('address', $post->ID);
        $possession_terms = wp_get_post_terms($post->ID, 'possession');
        $build_type_terms = wp_get_post_terms($post->ID, 'build_type');
        $ownership_terms = wp_get_post_terms($post->ID, 'ownership');

        // Add keyterm
        if($ownership_terms[0]->slug == 'rental') {
            $link = str_replace('%keyterms%/', 'apartments-for-rent/', $link);
        } else {
            $link = str_replace('%keyterms%/', 'homes-for-sale/', $link);

        // Add community
        if($community) {
            $link = str_replace('%community%/', $community[0]->post_name . '/', $link);
        } else {
            $link = $possession_terms ? str_replace('%community%/', $possession_terms[0]->slug . '/', $link) : $link;

        // Add address
        if($address) {
            $link = str_replace('%address%/', sanitize_title($address) . '/', $link);
        } else {
            $link = $build_type_terms ? str_replace('%address%/', $build_type_terms[0]->slug . '/', $link) : $link;
    return $link;
add_filter('post_link', 'inventory_link_rewrite', 10, 2);

Unfortunately creating a second post type is not an option.

  • But ideally, the first part of the path (which is the rewrite base) should be static - i.e. something-static/%keyterms%/ and not just %keyterms%/. Otherwise, you'd run into 404 errors because of rewrite conflicts.
    – Sally CJ
    Nov 20, 2019 at 0:50
  • That seems to be the case. Is there a way around it?
    – hmakein
    Nov 20, 2019 at 15:13
  • Sorry, I had not actually looked thoroughly on your code.. but anyway, see my answer and let me know.
    – Sally CJ
    Nov 20, 2019 at 23:49

1 Answer 1


The problem is with that add_rewrite_tag('%keyterms%', '(.*)'), which results in rewrite conflicts because the generated rewrite rules would match http://example.com/(anything-here) and that (anything-here) can be a Page (post type page) slug, hence http://example.com/page-slug would not work — you wouldn't get a 404 error page, but the proper Page wouldn't be displayed, either.

So looking at your code, instead of using that (.*) (which matches anything), you should instead specify the exact value, which in your case is either apartments-for-rent or homes-for-sale:

add_rewrite_tag('%keyterms%', '(apartments-for-rent|homes-for-sale)');

That should work, but be sure to flush the rewrite rules — just visit the permalink settings page.


Sorry, I wasn't aware WordPress isn't retaining the RegEx for all rewrite rules, specifically for attachments where WordPress removes the brackets (( and )):

// Relevant code in WP_Rewrite::generate_rewrite_rules().
$submatchbase = str_replace( array( '(', ')' ), '', $match );

And I know you changed the RegEx to ([aehomnprt]+s-for-[aelnrst]+) which does work, although not as precise as the (apartments-for-rent|homes-for-sale).

So apart from the alternate RegEx, you can make (apartments-for-rent|homes-for-sale) works like so (this code would go in the theme functions file):

add_filter( 'rewrite_rules_array', 'fix_inventory_rewrite_rules' );
function fix_inventory_rewrite_rules( $rules ) {
    $rules2 = [];
    $values = 'apartments-for-rent|homes-for-sale';

    $_re = $values . '/';
    $my_re = preg_quote( $_re, '/' );
    foreach ( $rules as $regex => $query ) {
        if ( preg_match( '/^' . $my_re . '/', $regex ) ) {
            $regex = str_replace( $_re, '(' . $values . ')/', $regex );
        $rules2[ $regex ] = $query;

    return $rules2;
  • 1
    Thank you for the fantastic explanation! This is so close to working - now I'm only having this issue with the "apartments-for-rent" pages. If I switch the order in add_rewrite_tag to '(homes-for-sale|apartments-for-rent)', then the problem affects the "homes-for-sale" pages instead.
    – hmakein
    Nov 21, 2019 at 17:25
  • 1
    So based on this question from 2013, the regex OR operator in the add_rewrite_tag function will only work for the last option of the OR statement. It doesn't look like that issue has been addressed, so I spent a few hours messing around with regex101 and replaced the OR operator with what I hope will be a specific enough regular expression. It works! I'm very happy to say that the rewrite base does not need to be static. Thanks so much for your help!
    – hmakein
    Nov 21, 2019 at 21:53
  • 1
    Hey, I completely missed that issue! Thanks a lot for pointing it and I'm glad you found that alternate RegEx. But I updated the answer with another solution which you might want to try.. :) Btw, +1 for the question ;)
    – Sally CJ
    Nov 22, 2019 at 5:30
  • 1
    You're a genius! Your code worked perfectly, and I definitely prefer the specificity of your solution. Thank you for the update!
    – hmakein
    Nov 22, 2019 at 16:09

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.