Rewrite Rules Are Redirecting and Not Passing VARs

I have the following code in my functions.php file.

<?php
add_action('init', 'flush_rewrite_rules');

function custom_add_rewrite_rules( $wp_rewrite ) {
    $new_rules = array( 
        'insurance-leads-types/auto-insurance-leads/([^/.]+)/?$' => 'index.php?pagename=auto-insurance-leads&state=' . $wp_rewrite->preg_index(1),
        'insurance-leads-types/home-insurance-leads/([^/.]+)/?$' => 'index.php?pagename=home-insurance-leads&state=' . $wp_rewrite->preg_index(1)
    );
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action('generate_rewrite_rules', 'custom_add_rewrite_rules');

function add_query_vars( $query_vars ) {
  $query_vars[] = 'state';
  return $query_vars;
}
add_filter( 'query_vars', 'add_query_vars' ); 

When I go to the page /insurance-leads-types/auto-insurance-leads/STATENAME/ the URL is redirected to /insurance-leads-types/auto-insurance-leads/ without the trailing STATENAME/ and the variable is not passed to $query_vars.

Read More

I used Monkeyman’s Rewrite Analyzer and everything looks fine:

pagename: auto-insurance-leads

state: (.[^/]+)

Any ideas why the URL is redirecting?

Related posts

Leave a Reply

3 comments

  1. You should probably use the built in rewrite api functions rather than using the generate_rewrite_rules filter. There are some issues with your rewrite regex. You need to start each with a carrot ^, to tell WP to match starting at the beginning of the URL.

    I’m not really sure what you’re trying to accomplish with the ([^/.]+). Anything that isn’t a slash or a period? If that’s the case, it’s probably not going to work the way you expect. Someone could enter insurance-leads-types/auto-insurance-leads/tx.state/ and it wouldn’t get matched becauase your regex doesn’t account for the stuff following the dot.

    Here is a working version that will match anything that isn’t a slash in the final group.

    <?php
    add_action( 'init', 'wpse42604_add_rewrite' );
    function wpse42604_add_rewrite()
    {
        add_rewrite_rule(
            '^insurance-leads-types/auto-insurance-leads/([^/]+)/?$',
            'index.php?pagename=auto-insurance-leads&state=$matches[1]',
            'top'
        );
        add_rewrite_rule(
            '^insurance-leads-types/home-insurance-leads/([^/]+)/?$',
            'index.php?pagename=home-insurance-leads&state=$matches[1]',
            'top'
        );
    }
    

    All the code as a plugin. I wrote a really big tutorial about the rewrite api that’s worth a look.

    Edit: Also, you might get some unpredictable behavior flushing rewrite rules one very page load like your code does.

  2. I would approach this using the add_rewrite_endpoint() function.

    It takes care of adding query vars and is a lot more straight forward to set up plus you won’t need to worry about page slugs changing etc…

    The code would be:

    // in functions.php
    add_action( 'init', 'wpse42604_add_endpoints' );
    function wpse42604_add_endpoints() {
       add_rewrite_endpoint( 'state', EP_PAGES );
    }
    
    // in template file eg. page.php   
    if ( '' != get_query_var( 'state' ) ) {
        // ... do stuff with the state query var
    }
    

    The one downside (depending on how you look at it) is you won’t get the exact URLs you’re specifying above, they will be like this:

    insurance-leads-types/auto-insurance-leads/state/tx/

    But it’s a more robust and flexible solution, plus that URL is still nice looking and readable.

    The problem you’re seeing is one I’ve run into before to do with WordPress’s processing of the rewrite rules performing a canonical redirect before looking at the rewrites. Unfortunately it’s a while since I traced through it so can’t remember specifics but it’s the redirect_canonical() function in wp-includes/canonical.php that was causing me a similar problem.

  3. I faced similar issue multiple times and in my case, the redirect was caused by redirect_canonical() function placed in wp-includes/canonical.php. This function tries to redirect to base (canonical) URL for given request and it considers your URL /insurance-leads-types/auto-insurance-leads/STATENAME/ leading to same page as /insurance-leads-types/auto-insurance-leads/ so it redirects it.

    Quickest fix is to disable canonical redirect when your specific query var is used – like this:

    add_filter('redirect_canonical', function($redirect_url, $requested_url) {
        if ( get_query_var('state') ) {
            return $requested_url;
        }
    }, 10, 2);
    

    Of course you can play with this filter and add different conditions. I also recommend to check redirect_canonical function code to see how exactlty it works.