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:
<?php
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:
<?php
/**
* Output rel=canonical for singular queries.
*
* @package WordPress
* @since 2.9.0
*/
function rel_canonical() {
if ( !is_singular() )
return;
global $wp_the_query;
if ( !$id = $wp_the_query->get_queried_object_id() )
return;
$link = get_permalink( $id );
if ( $page = get_query_var('cpage') )
$link = get_comments_pagenum_link( $page );
echo "<link rel="canonical" href="https://wordpress.stackexchange.com/questions/64535/$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:
<?php
add_action('init', 'wpse64535_add_rewrite_rules');
function wpse64535_add_rewrite_rules()
{
add_rewrite_rule(
'^roller-derbies/([^/]+)/?$',
'index.php?pagename=states&var_state=$matches[1]',
'top'
);
add_rewrite_rule(
'^roller-derbies/([^/]+)/([^/]+)/?$',
'index.php?pagename=cities&var_state=$matches[1]&var_city=$matches[2]',
'top'
);
}
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.
<?php
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.