I have two pages, one called cities and the other called states. They use the add_query_vars function to grab the city and or state from a database. The pages load fine. For example when someone goes to sitename.com/roller-derbies/arizona/florence/ and the events in that city display. But when I view the page source and look at the canonical link inserted in wp-head it says sitename.com/cities or sitename.com/states after the page name. This creates a problem where none of my pages are being indexed by google as they all point to canonical link for cities or states. What can I do so the canonical link matches the url?

Below is the code I am using.

add_filter('query_vars', 'add_query_vars');
function add_query_vars($aVars) {
    $aVars[] .= 'var_state';
    $aVars[] .= 'var_city';
    return $aVars;

add_filter('rewrite_rules_array', 'add_rewrite_rules');
function add_rewrite_rules($aRules) {
    $aNewRules = array(
    'roller-derbies/([^/]+)/?$' => 'index.php?pagename=states&var_state=$matches[1]', 
    'roller-derbies/([^/]+)/([^/]+)/?$' => 'index.php?pagename=cities&var_state=$matches[1]&var_city=$matches[2]', 
    $aRules = $aNewRules + $aRules;
    return $aRules;

1 Answer 1


WordPress thinks you're on those pages because of the pagename query var in your rewrite rules.

Your journey is going to start in wp-includes/default-filters.php, which every WP developer should known and love: it's where the WordPress care uses its own plugin API to hook in and change stuff.

Of interest to your question:

add_action( 'wp_head',             'rel_canonical'                          );

If you take a look at the rel canonical function in wp-includes/link-template.php you find this:

 * Output rel=canonical for singular queries.
 * @package WordPress
 * @since 2.9.0
function rel_canonical() {
    if ( !is_singular() )

    global $wp_the_query;
    if ( !$id = $wp_the_query->get_queried_object_id() )

    $link = get_permalink( $id );

    if ( $page = get_query_var('cpage') )
        $link = get_comments_pagenum_link( $page );

    echo "<link rel='canonical' href='$link' />\n";

Unfortunately no filters to modify the link. So what you'll need to do is hook in someplace late, but before wp_head fires, check for your query vars, unhook the rel_canonical function and roll your own.

First step, clean up your rewrites a bit:

add_action('init', 'wpse64535_add_rewrite_rules');
function wpse64535_add_rewrite_rules()


add_filter('query_vars', 'wpse64535_add_query_vars');
function wpse64535_add_query_vars($vars)
    $vars[] = 'var_state';
    $vars[] = 'var_city';
    return $vars;

Second part, hook into template_redirect and check for the custom query variables. If you find either, remove the default rel_canonical function and use your own.

add_action('template_redirect', 'wpse64535_maybe_fix');
function wpse64535_maybe_fix()
    if(get_query_var('var_state') || get_query_var('var_city'))
        remove_action('wp_head', 'rel_canonical');
        add_action('wp_head', 'wpse64535_fix_canonical');

function wpse64535_fix_canonical()
    $link = home_url('roller-derbies/');

    // might want to validate these before just using them?
    if($state = get_query_var('var_state'))
        $link .= "{$state}/";

    if($city = get_query_var('var_city'))
        $link .= "{$city}/";

    echo '<link rel="canonical" href="' . esc_url($link) . '" />';

All of the above as a plugin.

  • Thank you very much. This sounds promising. I will try it when I get back home. I thought perhaps I was going to need to use some regular expressions in the .htaccess file. I prefer your method. Sep 8, 2012 at 14:32
  • Feel free to mark as accepted :) Sep 8, 2012 at 20:14

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.